Reputation: 107
I am trying to use async
/ await
with the reduce
method. But somehow only the first iteration of the loop works. The papers object skips the await
once the first reduce
iteration is done. Is it possible to create an object using reduce
while gathering it's content from a promise while also await for the entire process?
Reduce function:
const papers = await page.concepts.reduce(async (acc, cur) => {
acc[cur.paperID] = await getPageData(`papers/${ cur.paperID }`, 'md')
return acc
}, {})
Note: getPageData
returns a promise.
Upvotes: 1
Views: 2483
Reputation: 151036
Since the async function returns a promise, so you would have to expect your accumulator is a promise. So you would do something like this:
console.log("Program starting at", Date());
function delayedResolver(a, ms) {
return new Promise(function(resolve, reject) {
setTimeout(() => {
console.log("resolving at", Date());
resolve(a);
}, ms);
});
}
arr = [1, 3, 5];
r = arr.reduce(async function(a, b) {
return await delayedResolver(a, 3000).then(v => v + b);
}, Promise.resolve(0));
console.log(r);
r.then(v => console.log("final value", v, "at", Date()));
You have to treat the accumulator as a promise, and return a new promise each time.
Note that the async function returns a promise and the reduce
immediately applies the async function to the next pair of (accumulator, entry)
, so all these promises returned by the async function are created almost instantly, so that's why the promises resolve all after 3 seconds, not one by one.
Upvotes: 1
Reputation: 931
You can create an array of promises and then use Promise.all
to resolve them. If any request fails Promise.all
will stop processing the remaining requests.
const papersPromises = page.concepts.map(cur =>
getPageData(`papers/${ cur.paperID }`, 'md')
)
const result = await Promise.all(papersPromises)
// result will be an array with the results
Upvotes: 2
Reputation: 4739
This is one way to fold array into an object, but, naturally, it will not handle async functions and reduce itself. Also, reduce doesn't return a Promise to await for
You can solve your case with Promise.all and Object.fromEntries
// map concepts to promises which will resolve to page data for each concept
// and await for them all at once (will run in parallel)
const pageResponses = await Promise.all(page.concepts.map(concept => getPageData(`papers/${concept.paperID}`, 'md')))
// construct object where keys are concept.paperIDs and values are pageDatas
const papers = Object.fromEntries(
page.concepts
.map((concept, i) => ([concept.paperID, pageResponses[i]]))
)
if your runtime doesn't support Object.fromEntries (and you are not using polyfill), you can do the last part like this
const papers = page.concepts.reduce((acc, concept, index) => acc[concept.paperID] = pageResponses[index], {})
Upvotes: 1