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.


[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 ()

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: { ... } }