'setTimeout not working inside forEach
I have a forEach that calls a function. There needs to be a delay between each time it is called. I've put it inside a setTimeout inside the forEach. It isn't respecting the timeout after the first wait. Instead it is waiting once, then running all at once. I've set the timeout to 5 seconds and I am using a console to confirm. 5 seconds of wait, then several foobar console logs all at once.
Why am I getting this behavior?
var index = 0;
json.objects.forEach(function(obj) {
setTimeout(function(){
console.log('foobar');
self.insertDesignJsonObject(obj, index);
}, 5000);
});
Solution 1:[1]
setTimeout is async. What it does is register a callback function and put it in background which will be triggered after delay. Whereas forEach is synchronous function. So what your code did is register callbacks "all at once" that each will be triggered after 5 seconds.
Two ways to avoid it:
Have an index to set the timer.
json.objects.forEach(function(obj, index) {
setTimeout(function(){
// do whatever
}, 5000 * (index + 1));
});
This way the delay factor is based on the index of your objects, so even you register them at same time, it will trigger based on their own delay. index + 1 to keep the same result as in question since it starts at 0.
setInterval to loop your objects
var i = 0;
var interval = setInterval(function(){
var obj = json.objects[i];
// do whatever
i++;
if(i === json.objects.length) clearInterval(interval);
}, 5000);
setInterval is similar to setTimeout, although it triggers periodically based on interval. In here we access object and update the index inside of the interval function. Also don't forget to clear interval in the end.
The difference between those two is setInterval only registered one function compare to setTimeout registered as many as number of items in the list.
Solution 2:[2]
forEach runs synchronously, looping through all of your elements and scheduling a timer callback for each of them five seconds later. So five seconds later, all those callbacks happen.
Here in 2022, there are two primary approaches here:
Use chained
setTimeoutwith anindexvariable.Use an
asyncfunction,await, and a loop.
Here's an example of #1:
const json = {
objects: [
{name: "first object"},
{name: "second object"},
{name: "third object"},
{name: "fourth object"},
{name: "fifth object"},
],
};
function insertDesignJsonObject(obj, index) {
console.log(obj.name, index);
}
let index = 0; // The index of the next element to show
function doNext() {
// Do this one, increment the index
/*self.*/insertDesignJsonObject(json.objects[index], index);
++index;
// If we still have one to do, do it after a delay
if (index < json.objects.length) {
setTimeout(doNext, 1000); // 5000 for a five-second delay
}
}
doNext();
Here's #2:
const json = {
objects: [
{name: "first object"},
{name: "second object"},
{name: "third object"},
{name: "fourth object"},
{name: "fifth object"},
],
};
function insertDesignJsonObject(obj, index) {
console.log(obj.name, index);
}
const delay = ms => new Promise(resolve => setTimeout(resolve, ms));
(async () => {
for (let index = 0; index < json.objects.length; ++index) {
// Wait to do this one until a delay after the last one
if (index > 0) {
await delay(1000); // 5000 for five seconds
}
// Do this one
/*self.*/insertDesignJsonObject(json.objects[index], index);
}
})();
Sources
This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.
Source: Stack Overflow
| Solution | Source |
|---|---|
| Solution 1 | jbyrd |
| Solution 2 | T.J. Crowder |
