9

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);
    }
});
2
  • If "listings" is really an array, you shouldn't iterate with "for ... in". Use a numeric index instead. Commented Nov 26, 2011 at 14:25
  • @Pointy Yes it is just an array, oops :) Commented Nov 26, 2011 at 14:31

4 Answers 4

13

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);
    }
});
Sign up to request clarification or add additional context in comments.

8 Comments

this will still trigger n timeouts almost parallel.
Thanks for the reply! There is an initial 5000ms delay, but the subsequent calls to the asyncFunction occurs instantly! Can the subsequent calls to the functions have delays such that each call is 5000ms apart?
it also has the classic loop/scope problem (the "listing" variable)
I noticed all the async functions get the same listing variable as pointy pointed out
@Nyxynyx Normally, you invoke a function by func_name(parameter). Instead of defining a named function, you can also create and invoke an anonymous function: (function(argument){...})(parameter). By passing listing to the anonymous function, the listing variable inside the anonymous function will not change when the listing variable in the loop's body is updated again.
|
7

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');
    }
});

Comments

1

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

2 Comments

Thanks, reduced a line of code by using i instead of counter. asyncFunction uses 2 callback values inside data and sends them to another server.
@Nyxynyx: Not entirely sure what you mean, but you can pass whatever you need to the callback func. My main point was that you already have a named function, so there's no need to add two levels of scope inline. You just need to modify asyncFunction so that it returns a function to be invoked by the setTimeout. This is a cleaner approach.
-1

it's the difference.the key point is what is asyncFunction do? can you paste it out?

var foo=function(){
    alert("BAR");
    return function(){
        alert("I AM!");
    };
}
setTimeout(foo(),4000);
setTimeout(foo,5000);

2 Comments

The first "setTimeout()" is incorrect, as it will immediately invoke "foo()" and pass its result to "setTimeout()". The second, passing a string, is generally considered deprecated.
yes, passing string will eval it and then run it on global scope. Very Bad Idea

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.