Why does the await call for my custom promise hang? - javascript

I am trying to test a function in my Express/Node backend using Mocha. I have created a stub of the actual parameter which is modified by the function: it has a send method which is called in getValue (the function I want to test) and a ready parameter which I initialize to a new promise when the stub is created and resolve when send is called on the stub.
I'm trying to await this promise, but it's just hanging (and then Mocha times out the test). The setTimeout below prints Promise { 'abc' }, which I think means the promise has resolved as I expect it to, but the await never completes.
This is the relevant code in the test file:
function ResStubTemplate() {
return {
_json: undefined,
_message: undefined,
json: function(x) {
this._json = x;
return this;
},
send: function(message) {
this._message = message;
this.ready = Promise.resolve("abc");
return this;
},
ready: new Promise(_ => {})
}
}
// This is the test
it("should get the value.", async function(done) {
let req = { query: { groupId: 12345 } };
res = ResStubTemplate();
controller.getValue(req, res);
setTimeout(() => console.log(res.ready), 1000); // prints Promise { 'abc' }
let x = await res.ready; // hangs??
console.log(x); // never prints
done();
}
This is the relevant code in the tested file:
exports.getValue = function(req, res) {
ValueModel.findOne({groupId: req.query.groupId})
.then(value => res.json({value: value}).send();
};
The error I get is:
Error: Timeout of 5000ms exceeded.
For async tests and hooks, ensure "done()" is called; if returning a Promise,
ensure it resolves. (/.../test/api/controller_test.js)

When the expression:
let x = await res.ready; // hangs??
… is evaluated, the value is the promise created by this code:
ready: new Promise(_ => {})
That promise never resolves, so it keeps waiting for it.
Later you do this:
this.ready = Promise.resolve("abc");
… which replaces that promise with a new (resolved) promise, but the new promise isn't the value that you are awaiting.

Related

I am awaiting for a return Promise from a class method from a module i am creating and i get it, but in a weird context. How can i await it properly?

So I am creating a module with a class method(schedule) with async function awaiting the return
//SCHEDULER.JS//
class Schedule {
constructor(project_id, queue_id) {
this.project_id = project_id;
this.queue_id = queue_id;
}
//ASYNC METHOD 1
schedule = async (date, rquest) => {
const project = this.project_id;
const queue = this.queue_id;
const location = "us-central1";
const url = rquest.url;
const payload = rquest.body;
// Construct the fully qualified queue name.
const parent = client.queuePath(project, location, queue);
const task = {
httpRequest: {
httpMethod: rquest.method,
url,
headers: rquest.headers,
},
};
try {
const request = await { parent, task };
const [response] = await client.createTask(request);
console.log("<THIS IS THE PROJECT ID> :", response.name);
return `${response.name}`;
} catch (error) {
console.log("we have an error amigo!", error);
}
};
//ASYNC METHOD 2
delete = async (one) => {
return console.log("delete function", one);
};
I imported my module on main.js and used my method. Once the results returns, I need to use it as a parameter to another method(delete) on the module I created(Scheduler.js).
//main.js//
const task_id = scheduler.schedule(date, request);
scheduler.delete(task_id);
task_id is returning a promise and I can't execute scheduler.delete(task_id) because it is pending promise still.
Important: How can I handle this promise properly as I am only tasked to create the module and not the main.js. The people who would create the main.js would just be expected to run my methods without handling promise returns.
TLDR
task_id is returning a promise
If it's a promise you can await it
//main.js//
async function main () {
const task_id = await scheduler.schedule(date, request); // <--- THIS!
scheduler.delete(task_id);
}
main();
Await & promises:
In fact, the await keyword only works on promises (you can await non-promises but it is a no-op by design). That's the whole reason for await - an alternative way to use promises. Because of this functions marked with the async keyword always returns a promise.
Or if you prefer not to await then just use it as a promise:
//main.js//
scheduler.schedule(date, request)
.then(task_id => scheduler.delete(task_id));
You can create another function, which will be called from main.js, and inside this function call your actual function and in then function of Promise return the value.

Calling async function from within async function returns undefined

(Forgive me if the title is inaccurate to the problem this one is boggling the mind)
My application requires that I check a value from the request against the database. For this I created an asynchronous function to query the database:
async function checktoken(){
return prisma.$exists.invalidtoken({token: "testtoken"}).then(result => {
return(result)
})
}
I know that the database call on its own works:
prisma.$exists.invalidtoken({token: "testtoken"}).then(result => {
console.log(result) // returns true
})
In the function that fires at every request I try and call checktoken():
async function getUser(token){
var promise = await checktoken()
var result = promise
console.log(result) //undefined
};
Amending the function to include an explicit call to the database works but only when var promise = await checktoken() is defined before it:
async function getUser(token){
var promise = await checktoken() //When this is removed result1 is undefinded
await prisma.$exists.invalidtoken({token: "testtoken"}).then(result1 => {
console.log("inside db call: "+result1) // true
})
};
I think I have a fundamental misunderstanding of async/await but I am not sure exactly what I am missing.
EDIT:
I have updated my approach taking the advice I received and it still does not work. I am beginning to think my ORM is doing something weird:
async function test(token) {
const status = await prisma.$exists.invalidtoken({ token: token });
console.log(status);
return status;
}
test("123") //logs false (as it should)
async function getUser(token){
var status = await test(token) //logs undefined
console.log(status) //logs undefined
};
An async function requires an await. If you are using the promise.then() technique, then what you would want to do is return a new Promise(), and within the .then call back function resolve the promise
function checktoken() {
return new Promise((resolve,reject) => {
prisma.$exists.invalidtoken({token: "testtoken"}).then(result => {
doSomeOtherStuff();
resolve(result);
});
});
}
Is functionally the same as
async checktoken() {
await prisma.$exists.invalidtoken({token: "testtoken"});
}

Is it possible to cancel a child promise when parent is cancelled?

I have this code (using Bluebird Promise):
const promise = loadSomething(id)
.then(something => {
loadParentOfSomething(something.parentId);
return something;
});
When I then do promise.cancel() the getSomething is cancelled, but the getSomethingParent is not.
Is there a way to, when getSomething promise is cancelled, I can also get the getSomethingParent promise to cancel?
Both load functions returns a cancellable async promise with an HTTP request, and the reason I want to cancel them is because they can sometimes take a while to load and when for example a user navigates away (SPA) the response is no longer needed.
I think what you are actually looking for
const promise1 = loadSomething(id);
const promise2 = promise1.then(something => { return loadParentOfSomething(something.parentId); });
// ^^^^^^
promise2.catch(e => void "ignore"); // prevent unhandled rejections
Then you can keep using promise1 to access the result, but also call promise2.cancel(). This cancellation will be possible even after promise1 settled.
Define a function as the second parameter of the then callback. Example:
const promise = getSomething(id)
.then(something => {
getSomethingParent(something.parentId);
return something;
}, error => {
console.error(error)
});
When you call promise.reject() then getSomethingParent will not be called.
Reference
If you prepare a dummy promise to reference loadSomethingOfParent you should be able to cancel it within loadSomething.
// Create a dummy promise to reference `loadParentOfSomething`
var dummyPromise = Promise.resolve();
// Pass `dummyPromise` to `loadSomething`
const promise = loadSomething(id, dummyPromise).then(something => {
dummyPromise = loadParentOfSomething(something.parentId);
return something;
});
loadSomething will need an onCancel handler which will execute when the promise is cancelled.
function loadSomething(id, promise) {
return new Promise(function(resolve, reject, onCancel) {
// Do your stuff
// The `.cancel()` handler
onCancel(function() {
promise.cancel();
});
});
}

using async and await in nodejs getting Promise { <pending> }

In my case , i am able to get the token but not the way i wanted ie i do not want to print promise pending and my output after running in tokenDisp.js is :
output: Promise { pending }
t5Npxk5FfnRTj8iHd8vyyfGxnhXR4KQf
login.js:
module.exports = async function doLogin() {
const token = await loginToken();
const myToken = JSON.parse(token);
return console.log(myToken);
};
tokenDisp.js:
const myToken = require('./login.js);
myToken();
Can someone help ?
All async functions return a promise and you still have to use .then() or await on the return value from the async function in order to use that. If you return a value from your async function, it will be the resolved value of the returned promise. If you throw an exception, the exception will be the reason for the rejection of the returned promise.
The use of await inside the function is a convenience INSIDE the async function. It does not magically make an asychronous operation into a synchronous one. So, your function returns a promise. To get the value out of it, use .then() on it.
module.exports = async function doLogin() {
const token = await loginToken();
const myToken = JSON.parse(token);
console.log(myToken);
return myToken; // this will be resolved value of returned promise
};
const myToken = require('./login.js);
myToken().then(token => {
// got token here
}).catch(err => {
console.log(err);
});
Note: your login.js module produces the same result as if it was written like this (without using async or await):
module.exports = function doLogin() {
return loginToken().then(token => {
const myToken = JSON.parse(token);
console.log(myToken);
return myToken; // this will be resolved value of returned promise
});
};

How to return a Promise as the result of another promise when it is resolved

Here a sample code:
function getExtendedPromise() {
const promise = new Promise((resolve) => {
setTimeout(() => {
resolve('Hi');
}, 1000)
});
promise.on = () => {
console.log('on listener called')
};
console.log('getExtendedPromise: ', promise);
return promise;
}
async function callExtendedPromise() {
await Promise.resolve();
return Promise.resolve(getExtendedPromise())
}
const promise = callExtendedPromise();
console.log('callExtendedPromise Try: ', promise);
callExtendedPromise().then((result) => {
console.log('Final Result: ', result)
});
Log Results:
callExtendedPromise Try: Promise { <pending> }
getExtendedPromise: Promise { <pending>, on: [Function] }
getExtendedPromise: Promise { <pending>, on: [Function] }
Final Result: Hi
As seen from the sample above, when the object is logged inside the getExtendedPromise() function, it has an on property. I then wrap it in a Promise.resolve, and return it to the caller because I want the caller to get the Promise object with the on property.
However, when calling callExtendedPromise(), I am not able to access that wrapped promise with the on property.
How can I get that promise instance with the on property?
getExtendedPromise() is the part of a library and not open for modification.
P.S. the choice of the structure of the extended promise is not my choice in the original code, but rather the structure returned from a library.
Unfortunately, it may not be possible to return an extended Promise from an async function. (Following taken from async function on MDN)
...the return value of an async function is implicitly wrapped in Promise.resolve.
If you can modify callExtendedPromise and can confine consuming the extended parts (i.e. any call to on) to that method, then I'd do that. Alternatively, you could use some sort of returned object (see below). I'd recommend against this in general (though the real root of the issue is trying to extend the individual Promise instance and then also use it as a Promise, which it sounds like you have no way to adjust), but it should be
async function callExtendedPromise() {
await Promise.resolve();
return {extendedPromise: getExtendedPromise()};
}
...
const extendedPromise = (await callExtendedPromise()).extendedPromise;
extendedPromise.on(); // 'on listener called'
extendedPromise.then(message => console.log(message)); // 'Hi!'

Categories

Resources