JavaScript array .reduce with async/await JavaScript array .reduce with async/await javascript javascript

JavaScript array .reduce with async/await


The problem is that your accumulator values are promises - they're return values of async functions. To get sequential evaluation (and all but the last iteration to be awaited at all), you need to use

const data = await array.reduce(async (accumP, current, index) => {  const accum = await accumP;  …}, Promise.resolve(…));

That said, for async/await I would in general recommend to use plain loops instead of array iteration methods, they're more performant and often simpler.


I like Bergi's answer, I think it's the right way to go.

I'd also like to mention a library of mine, called Awaity.js

Which lets you effortlessly use functions like reduce, map & filter with async / await:

import reduce from 'awaity/reduce';const posts = await reduce([1,2,3], async (posts, id) => {  const res = await fetch('/api/posts/' + id);  const post = await res.json();  return {    ...posts,    [id]: post  };}, {})posts // { 1: { ... }, 2: { ... }, 3: { ... } }


[Not addressing OPs exact prob; focused on others who land here.]

Reduce is commonly used when you need the result of the previous steps before you can process the next. In that case, you can string promises together a la:

promise = elts.reduce(    async (promise, elt) => {        return promise.then(async last => {            return await f(last, elt)        })    }, Promise.resolve(0)) // or "" or [] or ...

Here's an example with uses fs.promise.mkdir() (sure, much simpler to use mkdirSync, but in my case, it's across a network):

const Path = require('path')const Fs = require('fs')async function mkdirs (path) {    return path.split(/\//).filter(d => !!d).reduce(        async (promise, dir) => {            return promise.then(async parent => {                const ret = Path.join(parent, dir);                try {                    await Fs.promises.lstat(ret)                } catch (e) {                    console.log(`mkdir(${ret})`)                    await Fs.promises.mkdir(ret)                }                return ret            })        }, Promise.resolve(""))}mkdirs('dir1/dir2/dir3')

Below is another example which add 100 + 200 ... 500 and waits around a bit:

async function slowCounter () {    const ret = await ([100, 200, 300, 400, 500]).reduce(        async (promise, wait, idx) => {            return promise.then(async last => {                const ret = last + wait                console.log(`${idx}: waiting ${wait}ms to return ${ret}`)                await new Promise((res, rej) => setTimeout(res, wait))                return ret            })        }, Promise.resolve(0))    console.log(ret)}slowCounter ()