I have a chain of several maps, one of which needs to perform a database operation for each array element, so I'm using async await.
const resultsAsPromises = arr
.map(syncDouble)
.map(asyncMapper)
This isn't a problem if it is the last item in the chain because I can unwrap it with Promise.all
console.log('results', await Promise.all(resultsAsPromises))
However, there are other synchronous operations I need to perform afterward, so I'd like to have the promises' values unwrapped before moving on to the next .map
.
Is there a way to do this? I thought perhaps just making an extraction mapper like
function extractPromiseValues(value) {
return value.then(v => v);
}
would work, but alas, no.
var arr = [1, 2, 3, 4, 5];
function timeout(i) {
return new Promise((resolve) => {
setTimeout(() => {
return resolve(`number is ${i}`);
}, 1);
});
}
function syncDouble(i) {
return i * 2;
}
async function asyncMapper(i) {
return await timeout(i)
}
function extractPromiseValues(value) {
return value.then(v => v);
}
async function main() {
const resultsAsPromises = arr
.map(syncDouble)
.map(asyncMapper)
// .map(extractPromiseValues)
console.log('results', await Promise.all(resultsAsPromises))
}
main();
Rather than passing an identity function to .then()
, pass your synchronous operation instead, OR await
the promise in an async
function before passing it to your synchronous operation:
function syncCapitalize(s) {
return s.slice(0, 1).toUpperCase() + s.slice(1);
}
const resultsAsPromises = arr
.map(syncDouble)
.map(asyncMapper)
.map(p => p.then(syncCapitalize)); // OR
//.map(async p => syncCapitalize(await p));
In the context of your example, this would look like:
function timeout(i) {
return new Promise(resolve => {
setTimeout(() => {
resolve(`number is ${i}`);
}, 1);
});
}
function syncDouble(i) {
return i * 2;
}
function asyncMapper(i) {
return timeout(i);
}
function syncCapitalize(s) {
return s.slice(0, 1).toUpperCase() + s.slice(1);
}
async function main() {
const arr = [1, 2, 3, 4, 5];
const resultsAsPromises = arr
.map(syncDouble)
.map(asyncMapper)
.map(p => p.then(syncCapitalize)); // OR
//.map(async p => syncCapitalize(await p));
console.log('results', await Promise.all(resultsAsPromises));
}
main();
Alternatively, if we are interpreting the question as Ghassen Louhaichi has, you could use the TC39 pipeline operator (|>
) proposal to write the chain using one of the options below:
const results = arr
.map(syncDouble)
.map(asyncMapper)
|> Promise.all
|> await
.map(syncCapitalize);
const results = (arr
.map(syncDouble)
.map(asyncMapper)
|> await Promise.all(#))
.map(syncCapitalize);
Unfortunately, unless you are using a Babel plugin, or until one of these proposals is merged into the official ECMAScript specification, you have to wrap the chain with await Promise.all(...)
:
const results = (await Promise.all(arr
.map(syncDouble)
.map(asyncMapper)))
.map(syncCapitalize);
Finally, in the context of your full example:
function timeout(i) {
return new Promise(resolve => {
setTimeout(() => {
resolve(`number is ${i}`);
}, 1);
});
}
function syncDouble(i) {
return i * 2;
}
function asyncMapper(i) {
return timeout(i);
}
function syncCapitalize(s) {
return s.slice(0, 1).toUpperCase() + s.slice(1);
}
async function main() {
const arr = [1, 2, 3, 4, 5];
const results = (await Promise.all(arr
.map(syncDouble)
.map(asyncMapper)))
.map(syncCapitalize);
console.log('results', results);
}
main();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With