When explicitly returning a new Promise from a function we get resolve and reject functions passed in. We can use these methods inside our function at any point to indicate the resolution or rejection of the promise, including inside callbacks to other functions internally.
Consider:
const doSomeDbWork = function (db) {
return new Promise((resolve, reject) => {
db.doStuff(result => resolve(result));
});
};
Is there an equivalent when using async without explicitly returning a new Promise when we can't also await (like in the case of this db callback)?
const doSomeDbWork = async function (db) {
db.doStuff(result => /* ? */);
};
I'm currently thinking that this isn't possible and that I just need to return a new Promise. Have I missed a trick? Is there a way to do this?
Is there an equivalent when using async without explicitly returning a new Promise.
No. async/await only works with promises (or thenables), and you have to promisify everything else yourself. In your case, that would most likely be something like
async function doSomeDbWork(db) {
return new Promise(db.doStuff);
}
or
async function doSomeDbWork(db) {
return new Promise(resolve => { db.doStuff(resolve); });
}
(possible with an await after the return).
Related
Written below is an example of my code I usually do.
What I did is..
Add try-catch for a promise function with async-await.
Do no add try-catch for a promise function without async-await.
What I want to know is my code is alright, and it is anti-pattern or not.
Thank you in advance.
const readStatusAll = data => {
return new Promise( async (resolve, reject) => {
try {
const category = await CoreStatus.findAll()
resolve(category)
} catch(err) {
reject(err)
}
})
}
// Promise without Await
const readStatusAll = data => {
return new Promise( async (resolve, reject) => {
CoreStatus.findAll()
resolve(category)
}
})
}
What I want to know is my code is alright, and it is anti-pattern or not.
The first one will work properly, but is an anti-pattern.
The second one will not work properly.
Neither of the code blocks you show is the recommended way to do things because they are both needlessly wrapping an existing promise in another manually created promise. This is referred to as an anti-pattern. This first version will actually work properly, but it contains a bunch of useless code (thus making it an anti-pattern) and with a little more complexity in the function, it's very easy to make coding mistakes (which is why, in addition to the useless code it contains, its an anti-pattern).
const readStatusAll = data => {
return new Promise( async (resolve, reject) => {
try {
const category = await CoreStatus.findAll()
resolve(category)
} catch(err) {
reject(err)
}
})
}
It can instead be this:
const readStatusAll = data => {
return CoreStatus.findAll();
}
The caller will receive the promise as the return value and can then use either .then() and .catch() or await and try/catch. Since you aren't doing anything with the error, other than propagating it, you don't need to catch the error locally - you can just let it propagate back to the caller.
Your second version is just not correct at all:
// Promise without Await
const readStatusAll = data => {
return new Promise( async (resolve, reject) => {
CoreStatus.findAll()
resolve(category)
}
})
}
Because you're not paying any attention to any asynchronous return from CoreStatus.findAll() so you will resolve this manual wrapper promise long before the database call is actually done. In fact, this isn't even legal code as you have improper bracing.
Perhaps you meant to call resolve(category) in some callback or .then() handler associated with CoreStatus.findAll(). But, even if you did that, you still wouldn't be propagating errors back to the caller. This is not the way to do things.
I saw that putting async in front of a JS function returns a Promise.
What I have now:
async function asyncTest(p) {
return p;
}
let www = asyncTest(1)
console.log('www ', www); // returns Promise {<fulfilled>: 1}, see image
I was wondering if I could use this to have resolved and rejected in an async, acting like a typical new Promise((resolved, rejected)....
What I would like to do:
async function asyncTest(p, (resolved, rejected)=>{
resolved(p);
})
let www = asyncTest(1)
console.log('www ', www); // I want it to return 1
Is this even possible?
I was wondering if I could use this to have resolved and rejected in an async, acting like a typical new Promise((resolved, rejected)…
No. An async function does not provide you with callbacks to resolve/reject a promise, it only provides you with await. You can re-create Promise.resolve and Promise.reject with
async function resolve(x) { return x; }
async function reject(x) { throw x; }`
but that's it. To promisify a callback API, you always need to use the new Promise constructor.
Is there any typescript config option or some workaround to check if there is no resolve called in new Promise callback?
Assume I have a promise
new Promise(async (resolve, reject) => {
try {
// do some stuff
// but not calling resolve()
} catch (e) {
reject(e)
}
})
I want typescript to warn me that I did not call resolve(). Is it possible?
I know that I can use noUnusedParameters, but there are a couple of situations where I still need unused parameters (e.g. request inside of express.Hanlder, where I only use response, etc.)
No, that is not possible. Knowing whether code calls a certain function (resolve in this case) is just as hard as the halting problem. There is proof that no algorithm exists that can always determine this.
To illustrate, let's assume that the algorithm for determining whether a function calls resolve exists, and is made available via the function callsResolve(func). So callsResolve(func) will return true when it determines that func will call resolve (without actually running func), and false when it determines that func will not call resolve.
Now image this func:
function func() {
if (!callsResolve(func)) resolve();
}
... now we have a paradox: whatever this call of callsResolve returned, it was wrong. So for instance, if the implementation of callsResolve would have simulated an execution of func (synchronously) and determines that after a predefined timeout it should return false, the above is a demonstration of a function that calls resolve just after that timeout expired.
The closest you can get to a compile time check is to use async / await syntax.
If you don't want to use that, you could timeout your promises, though you would have to do that with each of your promise after / when you are creating them.
A solution could look like this:
export const resolveAfterDelay = (timeout: number) => new Promise((r) => setTimeout(r, timeout));
export const rejectAfterDelay = async (timeout: number) => {
return new Promise((resolve, reject) => setTimeout(() => reject(`Promise timed out as resolve was not called within ${timeout}ms`), timeout));
};
export const timeoutPromise = <T>(timeout: number) => async (p: Promise<T>): Promise<T> => {
return Promise.race([p, rejectAfterDelay(timeout)]);
};
const timeoutAfter1s = timeoutPromise(1e3);
const timeoutAfter10s = timeoutPromise(10e3);
timeoutAfter10s(resolveAfterDelay(3e3)).then(success => console.log("SUCCESS IS PRINTED")).catch(console.error); // works
timeoutAfter1s(resolveAfterDelay(3e3)).then(success => console.log("NEVER REACHED")).catch(console.error); // aborts
const neverResolvingPromise = new Promise(() => {
});
timeoutAfter1s(neverResolvingPromise).catch(console.error); // promise never resolves but it will be rejected by the timeout
It makes use of Promise.race. Basically, whatever first resoves or rejects will be returned. We want to always reject a Promise if it does not resolve in time.
You would always have to wrap your Promise on creation like
timeoutAfter10s(new Promise(...));
And you would have to adapt the timeout according to your use case.
Working case:
async await is working fine when we call a asynchronous function and that function returning a promise resolve()
Not working case:
async await is not working for mongo DB queries
tried then(), async/await
I have 2 JS files.
In one.js file i am importing function which is in functionone.js
WORKING CASE:
When one.js looks like
var functiononestatus = transactions.functionone(req.session.email).then((came) => {
console.log(came); // getting `need to be done at first` message
console.log("exec next")
});
When functionone.js looks like
module.exports.functionone = functionone;
async function functionone(email) {
return await new Promise((resolve, reject) => {
resolve('need to be done at first')
});
});
NOT WORKING CASE (when mongo db query need to be executed):
When one.js looks like
var functiononestatus = transactions.functionone(req.session.email).then((came) => {
console.log(came); // getting undefined
console.log("exec next")
});
When functionone.js looks like
module.exports.functionone = functionone;
async function functionone(email) {
//mongo starts
var collection = await connection.get().collection('allinonestores');
await collection.find({
"email": email
}).toArray(async function(err, wallcheck) {
return await new Promise((resolve, reject) => {
resolve(wallcheck[0])
});
});
Quick clarification:
.collection('name') returns a Collection instance, not a Promise, so no need to await for it.
toArray() operates in two modes: either with a callback when a function is provided, either returns a Promise when no callback function is provided.
You're essentially expecting a Promise result out of toArray() while supplying a callback function, resulting in undefined, because callback takes priority and no promise is returned, due to the dual operation mode of toArray().
Also, toArray(callback) does not take an async function as callback.
Here's how your code should look like, for retrieving a collection:
const client = await MongoClient.connect('your mongodb url');
const db = client.db('your database name'); // No await here, because it returns a Db instance.
const collection = db.collection('allinonestores'); // No await here, because it returns a Collection instance.
and then, code for fetching results:
const db = <get db somehow>;
// You could even ditch the "async" keyword here,
// because you do not do/need any awaits inside the function.
// toArray() without a callback function argument already returns a promise.
async function functionOne(email) {
// Returns a Collection instance, not a Promise, so no need for await.
const collection = db.collection('allinonestore');
// Without a callback, toArray() returns a Promise.
// Because our functionOne is an "async" function, you do not need "await" for the return value.
return collection.find({"email": email}).toArray();
}
and code alternative, using callback:
const db = <get db somehow>;
// You could even ditch the "async" keyword here,
// because you do not do/need any awaits inside the function.
// You're forced to return a new Promise, in order to wrap the callback
// handling inside it
async function functionOne(email) {
// Returns a Collection instance, not a Promise, so no need for await.
const collection = db.collection('allinonestore');
// We need to create the promise outside the callback here.
return new Promise((resolve, reject) => {
db.find({"email": email}).toArray(function toArrayCallback(err, documents) {
if (!err) {
// No error occurred, so we can solve the promise now.
resolve(documents);
} else {
// Failed to execute the find query OR fetching results as array someway failed.
// Reject the promise.
reject(err);
}
});
});
}
Note: First of all i really need to thank #mihai Potra for the best answer.
Here we go
Case 1:
If it is a function which need to find documents and return from MongoDb as mihai mentioned, below answer is uber cool
const db = <get db somehow>;
async function functionOne(email) {
const collection = db.collection('allinonestore');
return collection.find({"email": email}).toArray();
}
case 2:
If there are nested functions which need to return values every time below ans will be the best as of my knowledge
-no need async/await keywords for every function or no need then()
function one(<parameter>) {
return new Promise(function(resolve, reject) {
const collection = connection.get().collection('<collection_name>');
const docs = collection.find({"Key": Value_fromFunction}).toArray( function (err, result) {
resolve(result[0]);
});
That's it, use resolve callback when ever it needed.
There seems something inherently wrong with having to define a Promise's callback as asynchronous:
return new Promise(async (resolve, reject) => {
const value = await somethingAsynchronous();
if (value === something) {
return resolve('It worked!');
} else {
return reject('Nope. Try again.');
}
});
This is apparently an antipattern and there are coding problems which can arise from it. I understand that it becomes easier to fail to catch errors here, even when placing await statements inside try/catch blocks.
My first question is, what's the best way to code something like this, when one wants to forward a Promise with different resolve/reject values? With then/catch? I.e.
return new Promise((resolve, reject) => {
somethingAsynchronous().then(value => {
if (value === something) {
return resolve('It worked!');
} else {
return reject('Nope. Try again.');
}
}); // errors would now be propagated up
});
Or do you just take it out the Promise constructor altogether as suggested here?
async function outerFunction() {
const value = await somethingAsynchronous();
return new Promise((resolve, reject) => {
if (value === something) {
return resolve('It worked!');
} else {
return reject('Nope. Try again.');
}
});
}
But what if you have several await statements in the outerFunction(), i.e. a linear code block calling several asynchronous functions. Would you then have to create and return a new Promise every time?
But then how do you account for code such as this?
async function outerFunction() {
if (someSynchronousCheck()) {
return 'Nope. Try again.' // another reject case
}
const value = await somethingAsynchronous();
// ...
}
I have the feeling that I'm making this more complicated than it should be. I'm trying to avoid nesting callbacks/chaining then/catch blocks without creating more problems in the future.
My final question is, why is the callback passed to a Promise not inherently async? It is already wrapped within a promise and expects the resolve/reject functions to be called asynchronously.
You do this:
async function outerFunction() {
const value = await somethingAsynchronous();
if (value === something) {
return 'It Worked!';
}
throw Error('Nope. Try again.');
}
Using async wraps the result of outerFunction with a Promise.
If you want that wrapping promise to resolve to something, just return it from the async function. If you want the wrapping promise to be rejected, throw an error inside the async function.
But then how do you account for code such as this?
async function outerFunction() {
if (someSynchronousCheck()) {
throw Error('Nope. Try again.');
}
const value = await somethingAsynchronous();
// ...
}
new Promise(async (resolve, reject) => { ... }) is relatively new antipattern. It results in creating 2 promise objects instead of 1, uncaught errors that happen inside constructor cannot be caught with try..catch and result in unhandled rejection.
Considering that promise asynchronous code can be handled with async..await, current use case for Promise constructor is non-promise asynchronous code, e.g.:
new Promise(resolve => setTimeout(resolve, 1000))
When Promise constructor contains synchronous code or involves other promises, a promise should be constructed with async function. A drop-in replacement is async IIFE:
return (async (resolve, reject) => {
const value = await somethingAsynchronous();
if (value === something) {
return 'It worked!';
} else {
throw 'Nope. Try again.';
}
})();
If the need for Promise constructor still presents when being used together with async, Promise constructor should be moved down in hierarchy so it won't wrap any async function.
My final question is, why is the callback passed to a Promise not inherently async? It is already wrapped within a promise and expects the resolve/reject functions to be called asynchronously.
async function isn't just a function that is executed asynchronously, it returns another promise that is supposed to be utilized - or at least handled with catch. Promise isn't supposed to utilize a promise that is returned from constructing function.
The constructor can resolve on same tick and doesn't necessarily have to be asynchronous.
Promise.resolve(1);
is similar to
Promise(resolve => resolve(1))
and not to
Promise(resolve => setTimeout(() => resolve(1)))
You can also chain the promises yourself by simply doing this:
return new Promise((resolve, reject) => {
somethingAsynchronous().then((value) => {
if (value === something) {
return resolve('It worked!');
} else {
return reject('Nope. Try again.');
}
}, (error) => { reject(error); });
});
I've been using this for some time and it works perfectly for me.