I have a asynchronous function that I want to have a 5000ms delay before being fired. I am trying to use setTimeout()
to achieve this. This async function occurs in a loop that runs several times, with the async function being passed different data each time, thus setInterval()
cannot be used here.
Problem: The async function gets triggered instantly without delay (console prints 5 Done
messages instantly` and loops without any delay. What happened, and how can I solve it?
Javascript Code
someFunction(listings, function() {
for (var i in listings ) {
var listing = listings[i];
setTimeout(asyncFunction(listing, function(data) {
console.log('Done');
}), 5000);
}
});
You have to wrap the function in another function. Currently, you're invoking the function, and passing the return value as an argument to setTimeout
. The code below will (correctly) pass a function to setTimeout
. After 5 seconds, the function executes.
I had to add two functions to achieve the desired behaviour, because of scoping issues. After 5 seconds, the loop has already finished, and the listing
variable would be equal to the last element in listings
.
someFunction(listings, function() {
var counter = 0; // Define counter for 5 second-delays between each call
for (var i in listings ) {
var listing = listings[i];
(function(listing){ //Closure function
setTimeout(function(){ //setTimeout function
// Because of the closure, `listing` is unique
asyncFunction(listing, function(a, b) {
console.log('Done');
});
}, 5000 * ++counter); //Increase counter for each loop
})(listing);
}
});
If you are using ECMAScript6 you can use Promise.
So create a delay function that wrap the call to the setTimeout into a Promise:
function delay(ms) {
return new Promise(function (resolve) { return setTimeout(resolve, ms); });
};
And you can use it like that :
someFunction(listings, function() {
for (var i in listings ) {
var listing = listings[i];
delay(5000).then(() => {
return asyncFunction(listing);
}).then(() => {
console.log('Done');
});
}
});
If you are using ECMAScript 2017 you can use aync/await.
Async functions return promise so you don't have to change the code of the delay function.
async someFunction(listings, function() {
for (var i in listings ) {
var listing = listings[i];
await delay(5000);
await asyncFunction(listing);
console.log('Done');
}
});
Not knowing what your asyncFunction
does, it would seem that it could simply return the function you passed it.
someFunction(listings, function() {
for (var i = 0; i < listings.length; ++i ) {
setTimeout(asyncFunction(listings[i], function(data) {
console.log('Done');
}), 5000 * i);
}
});
function asyncFunction( lstng, func ) {
return func;
}
Though I'd expect that you need to wrap up some additional logic.
function asyncFunction( lstng, func ) {
return function() {
// do some stuff with the listing
// then invoke the func
func();
}
}
Now your asyncFunction
wraps whatever is needed in a new function that is returned to the setTimeout
. The new function also invokes the callback you passed.
JSFIDDLE DEMO
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With