Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
JavaScript: async/await with forEach()
const waitFor = (ms) => new Promise(r => setTimeout(r, ms))
const asyncForEach = async (array, callback) => {
for (let index = 0; index < array.length; index++) {
await callback(array[index], index, array)
}
}
const start = async () => {
await asyncForEach([1, 2, 3], async (num) => {
await waitFor(50)
console.log(num)
})
console.log('Done')
}
start()
@medmin

This comment has been minimized.

Copy link

medmin commented Jan 4, 2018

Hi, what's 'r' ?

r => setTimeout(r, ms)

@stevendelro

This comment has been minimized.

Copy link

stevendelro commented Jan 5, 2018

Thanks for your help!

I've used your example for a redux application that I'm working on and I can't get dispatch inside my function in order to send out the computed data.

BTW, I'm totally not expecting a response, but this is my shot in the dark. I would appreciate any pointers if you had any.

export const fetchDetailsByIDs = async listIDs => {
  let fullDetailArray = [];
  let detailsByIds = {};
  await asyncForEach(listIDs, async imdbID => {
    const url = `${ROOT_URL}?i=${imdbID}${API_KEY}`;
    await axios.get(url).then(response => {
      fullDetailArray.push(response.data);
    });
    //Create a state object for detail reducer
    for (var i = 0; i < fullDetailArray.length; i++) {
      detailsByIds[fullDetailArray[i].imdbID] = fullDetailArray[i];
    }
  });
// The logs below show me the correct data, but I can't get dispatch 
// inside this function at all in order to use it. 
  console.log('DETAIL ARRAY', fullDetailArray);
  console.log('DETAIL STATE OBJECT', detailsByIds);
};


export const fetchMainDataSuccess = movieList => {
  type: FETCH_MAIN_DATA_SUCCESS;
  payload: movieList;
};
@stevendelro

This comment has been minimized.

Copy link

stevendelro commented Jan 6, 2018

I figured it out. I just combined the async/await logic into the promise chain of the reducer before it. I couldn't have done it without your asyncForEach insight. Thanks!

If you were curious:

export const fetchData = searchTerm => {
  const url = `${ROOT_URL}?s=${searchTerm}${API_KEY}`;
  return dispatch => {
    dispatch(fetchStarted());
    axios
      .get(url)
      .then(
        response => Object.keys(mapKeys(response.data.Search, 'imdbID')),
        dispatch(fetchMainData())
      )
      .then(async movieIDs => {
        let fullDetailArray = [];
        await asyncForEach(movieIDs, async imdbID => {
          const url = `${ROOT_URL}?i=${imdbID}${API_KEY}`;
          await axios.get(url).then(response => {
            fullDetailArray.push(response.data);
          });
        });
        console.log('DETAIL ARRAY', fullDetailArray);
        dispatch(fetchMainDataSuccess(fullDetailArray));
      });
  };
};
@patrickbense

This comment has been minimized.

Copy link

patrickbense commented Jan 16, 2018

@medmin

const waitFor = (ms) => new Promise(r => setTimeout(r, ms))

r in the above is the resolve method for promises. That is, a Promise would typically look like this new Promise(resolve, reject) { resolve() }. In the case that you are querying about, reject is omitted. This "waitFor" method is a clever way to get JavaScript to pause for whatever amount of milliseconds you want before proceeding to the next line. While using in an asyncForEach like this example, you force the loop to stop executing for x milliseconds before it prints out the next line then re-enters the for loop again for the next value of the array.

Hope that helps.

@borel

This comment has been minimized.

Copy link

borel commented Apr 15, 2018

Hi Patrick ,

Is it better with async on line 2 const asyncForEach = async (array, callback) => { ?

PB

@skydaddy

This comment has been minimized.

Copy link

skydaddy commented Apr 24, 2018

God, Allah, The universe, or whoever you do believe in bless you. A star for your fine work. Solved my problem perfectly with various ForEach that I needed to execute in which each one depended on the result of the previous.

@randheerschouhan

This comment has been minimized.

Copy link

randheerschouhan commented Jul 31, 2018

this

const asyncForEach = (array, callback) => {
should be this :
const asyncForEach = async (array, callback) => {

@farzadso

This comment has been minimized.

Copy link

farzadso commented Aug 23, 2018

I read the gist without reading the author's name.

Then I'm like : "This is something Pooya or Sebastian would do"

And to my surprise it was made by you

@mesqueeb

This comment has been minimized.

Copy link

mesqueeb commented Sep 10, 2018

@Atinux Any chance you can turn this into an npm module?
I've been using this throughout a lot of projects and always add it as a manual helper file. I think that means it's a great candidate for an npm module!

@mattyglover

This comment has been minimized.

Copy link

mattyglover commented Oct 11, 2018

I don't get how you can have an await in the first function?

await callback(array[index], index, array)
^^^^^

SyntaxError: await is only valid in async function
at new Script (vm.js:74:7)
at createScript (vm.js:246:10)
at Object.runInThisContext (vm.js:298:10)
at Module._compile (internal/modules/cjs/loader.js:657:28)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10)
at Module.load (internal/modules/cjs/loader.js:599:32)
at tryModuleLoad (internal/modules/cjs/loader.js:538:12)
at Function.Module._load (internal/modules/cjs/loader.js:530:3)
at Function.Module.runMain (internal/modules/cjs/loader.js:742:12)
at startup (internal/bootstrap/node.js:266:19)

The issue with prefixing the callback with async to enable that await to work means that the net result does not accomplish a serial execution as planned.

@gwynnebaer

This comment has been minimized.

Copy link

gwynnebaer commented Oct 11, 2018

@mattyglover - the correct version should be:


async function asyncForEach(array, callback) {
  for (let index = 0; index < array.length; index++) {
    await callback(array[index], index, array)
  }
}

const start = async () => {
  await asyncForEach([1, 2, 3], async (num) => {
    await waitFor(50)
    console.log(num)
  })
  console.log('Done')
}
start()
@SHEHANhasintha

This comment has been minimized.

Copy link

SHEHANhasintha commented Oct 16, 2018

await callback(array[index], index, array)
^^^^^


SyntaxError: await is only valid in async function
at new Script (vm.js:74:7)
at createScript (vm.js:246:10)
at Object.runInThisContext (vm.js:298:10)
at Module._compile (internal/modules/cjs/loader.js:657:28)
at Object.Module._extensions..js (internal/modules/cjs/loader.js:700:10)
at Module.load (internal/modules/cjs/loader.js:599:32)
at tryModuleLoad (internal/modules/cjs/loader.js:538:12)
at Function.Module._load (internal/modules/cjs/loader.js:530:3)
at Function.Module.runMain (internal/modules/cjs/loader.js:742:12)
at startup (internal/bootstrap/node.js:266:19)

The issue with prefixing the callback with async to enable that await to work means that the net result does not accomplish a serial execution as planned.

This document has an error. you can't have a function without async while await in them.
to fix this, you can do, this. add async infront of the missing function.

const waitFor = (ms) => new Promise(r => setTimeout(r, ms))
const asyncForEach = async (array, callback) => {
for (let index = 0; index < array.length; index++) {
await callback(array[index], index, array)
}
}

const start = async () => {
await asyncForEach([1, 2, 3], async (num) => {
await waitFor(5000)
console.log(num)
})
console.log('Done')
}

start()

@radiumrasheed

This comment has been minimized.

Copy link

radiumrasheed commented Nov 20, 2018

how can one get the index from this method

@JonnWalker10

This comment has been minimized.

Copy link

JonnWalker10 commented Nov 29, 2018

@radiumrasheed

how can one get the index from this method

The function will return it in the other params, which in SHEHAN's example where omitted:
if you add the ind param to the function it can be used!

const start = async () => {
await asyncForEach([1, 2, 3], async (num,ind) => {
await waitFor(5000)
console.log(num)
console.log(ind)
})
console.log('Done')
}

@Mifrill

This comment has been minimized.

Copy link

Mifrill commented Jun 12, 2019

Thank you, man, great implementation!

@Mifrill

This comment has been minimized.

Copy link

Mifrill commented Jun 16, 2019

use for (let element of elements), it's simpler, shorter:

const start = async () => {
  for (let num of [1, 2, 3]) {
    await waitFor(50);
    console.log(num);
  }
  console.log('Done');
}
start();
@wiill

This comment has been minimized.

Copy link

wiill commented Jun 16, 2019

Thanks for this piece of code, works like a charm...

would it be bad design / coding do define a prototype function for array like this:
Array.prototype.forEachAsync = async function(callback){ // this represents our array for (let index = 0; index < this.length; index++) { // We call AND AWAIT the callback for each entry await callback(this[index], index, this); } };

to later have calls like this:
await allFiles.forEachAsync(async (aFile) => { //DO AND WAIT FOR ASYNC STUFF BEFORE CARRYING ON TO NEXT aFile }

@gustavocagninzup

This comment has been minimized.

Copy link

gustavocagninzup commented Jul 19, 2019

I do believe the original forEach from ES6 should understand the async/await call and wait for the inner called code to resolve, and then iterate with a new value.

@pfeilbr

This comment has been minimized.

Copy link

pfeilbr commented Nov 7, 2019

Hi, what's 'r' ?

r => setTimeout(r, ms)

r is the resolve parameter (1st param) of the Promise constructor callback function.

@axle21

This comment has been minimized.

Copy link

axle21 commented Nov 12, 2019

Hello is there a way that it can be apply using an axios? cos base on what i understand the waitFor is set statically, so how will it wait for a axios response

@wodCZ

This comment has been minimized.

Copy link

wodCZ commented Nov 21, 2019

Thanks for the snippet and article!

TypeScript version for anyone interested:

export async function asyncForEach<T>(array: T[], callback: (item: T, index: number, allItems: T[]) => void) {
  for (let index = 0; index < array.length; index++) {
    await callback(array[index], index, array);
  }
}
@dblodorn

This comment has been minimized.

Copy link

dblodorn commented Dec 5, 2019

Thanks so much for this snippet and explanation! Implemented in this webpack plugin I wrote that creates a json file from a data endpoint. I wanted to use an array and this was perfect!

https://github.com/dblodorn/fetch-json-webpack-plugin

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
You can’t perform that action at this time.