Using Async / Awaits within promise
Using Async / Awaits within promise
I have a certain promise chain in my code that looks like this:
myPromise()
.then(getStuffFromDb)
.then(manipulateResultSet)
.then(manipulateWithAsync)
.then(returnStuffToCaller)
Now, in my manipulateWithAsync I'm trying to enhance my result set by calling the DB again, but it's not working as I expected, since while debugging i figured out the control moves to the next function which is the returnStuffToCaller
here's an idea of what's into my manipulateWithAsync function:
function manipulateWithAsync(rs) {
return rs.map( async function whoCares(singleRecord) {
let smthUseful = await getMoreData(singleRecord.someField);
singleRecord.enhancedField = smthUseful;
return singleRecord;
})
}
I get the point of this behaviour: the map function does work as expected and the promise chain doesn't give a duck about it since it's not working with the awaits.
Is there a way to allow my returnStuffToCaller function to wait till the async function did his job?
I also use bluebird and i tried to use coo-routine, so if you thing that it's a good solution I'll post my bluebird coo-routine failing code :)
Thanks!
return Promise.all(rs.map(...))
return
undefined
yeah man, i clearly return something in my map. i wrote the code on the fly while creating the post, thanks for pointing out, gonna edit right away
– Koop4
Jul 3 at 9:19
But as @PatrickRoberts pointed out, you need to have
Promise.all
, otherwise the promise resolves with the array returned by manipulateWithAsync
.– Thiago Barcala
Jul 3 at 9:22
Promise.all
manipulateWithAsync
Confirmed, just modified my code and it works properly. @Patrick if you can create an answer i'd be happy to approve it.
– Koop4
Jul 3 at 9:23
3 Answers
3
The problem is in using async/await with Array.map
Array.map
This answer should help: https://stackoverflow.com/a/40140562/5783272
yep, my question is actually a duplicate
– Koop4
Jul 3 at 9:26
In this case
Promise.map(arr, fn)
can be used– Benjamin Gruenbaum
Jul 3 at 12:31
Promise.map(arr, fn)
rs.map iterator jumps to the next element without waiting in each separate iteration.
You need something like asyncMap
You can use - https://github.com/caolan/async
or either implement yourself
async function asyncMap(array, cb) {
for (let index = 0; index < array.length; index++) {
return await cb(array[index], index, array);
}
}
*cb function must be async one
This one works too, I think an easier change would be to use forEach over map. Thanks!
– Koop4
Jul 3 at 9:26
Bluebird already comes with
Promise.map
though– Benjamin Gruenbaum
Jul 3 at 12:31
Promise.map
Wrap your map with Promise.all
return the Promise
then await
for the results wherever you call the manipulateWithAsync
.
Promise.all
Promise
await
manipulateWithAsync
// MOCKS FOR DEMO
// Test data used as input for manipulateWithAsync
const testData = [
{ recordNumber: 1 },
{ recordNumber: 2 },
{ recordNumber: 3 }
];
// Mock function which returns Promises which resolve after random delay ranging from 1 - 3 seconds
const getMoreData = () =>
new Promise(resolve => {
const calledAt = Date.now();
setTimeout(() => {
resolve({
usefulData: `Promise called at ${calledAt}`
});
}, Math.floor(Math.random() * 3000) + 1000);
});
// SOLUTION / ANSWER
const manipulateWithAsync = async rs =>
Promise.all(
rs.map(async singleRecord => {
const smthUseful = await getMoreData(singleRecord.someField);
// Instead of manipulating original data,
// which might cause some unwanted side effects going forward,
// instead return new objects
return { ...singleRecord, enhancedField: smthUseful };
})
);
await manipulateWithAsync(testData);
By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.
return Promise.all(rs.map(...))
but it might help toreturn
something from your map function, otherwise you're going to resolve with an array filled withundefined
– Patrick Roberts
Jul 3 at 9:13