Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I delay an array callback using async/await in JavaScript?

I'm trying to put a delay between each iteration in a loop using async await. I've got a helper sleep function:

const sleep = ms => {
  return new Promise(resolve => {
    setTimeout(resolve, ms);
  });
}

And this is correctly waiting between each loop:

for (let i = 0; i < 5; i++) {
    console.log('waiting')
    await sleep(1000)
}

However, this is not waiting between each loop:

[0, 1, 2, 3, 4].forEach(async () => {
    console.log('waiting')
    await sleep(1000)
});

How can I modify the forEach code block to behave as the regular for loop block with delays between each iteration of the for loop?

like image 296
Andrew Avatar asked Nov 27 '25 19:11

Andrew


2 Answers

You could theoretically build up a promise chain, thats slightly more beautiful with reduce, but the same pattern can also be done with forEach:

[0, 1, 2, 3, 4].reduce(async (previous) => {
  await previous;
  console.log('waiting')
  await sleep(1000)
});

But ... why not just use a for loop?

like image 57
Jonas Wilms Avatar answered Nov 29 '25 13:11

Jonas Wilms


If you prefer methods rather than loops (which I usually do just aesthetically) you could pull in a third party module I maintain called async-af.

Among other things, it provides an asynchronous-friendly sequential forEach:

const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));

AsyncAF([0, 1, 2, 3, 4]).series.forEach(async () => {
  console.log('waiting');
  await sleep(1000);
});
<script src="https://unpkg.com/[email protected]/index.js"></script>

Of course, you could also just use a simple for...of loop:

const sleep = ms => new Promise(resolve => setTimeout(resolve, ms));

(async () => {
  for (const _ of [0, 1, 2, 3, 4]) {
    console.log('waiting');
    await sleep(1000);
  }
})();

As for why Array.prototype.forEach doesn't work the way you'd expect, take this overly simplified implementation (here's a fuller one):

const forEach = (arr, fn) => {
  for (let i = 0; i < arr.length; i++) {
    // nothing is awaiting this function call
    fn(arr[i], i, arr);
    // i is then synchronously incremented and the next function is called
  }
  // return undefined
};

forEach([0, 1, 2, 3, 4], async () => {
  console.log('waiting');
  await delay(1000);
});

As you can see, Array.prototype.forEach synchronously calls the given callback function on each element. That's why you see all five waiting logs almost immediately. See this question for more information.

like image 38
Scott Rudiger Avatar answered Nov 29 '25 12:11

Scott Rudiger



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!