timkl
timkl

Reputation: 3339

setTimeout delay doesn't wait for the timeout

I trying to wrap my head around setTimeout, but I can't get it to work properly.

I have set up an example here: http://jsfiddle.net/timkl/Fca2n/

I want a text to countdown after an anchor is clicked - but my setTimeout seems to fire at the same time, even though I've set the delay to 1 sec.

This is my HTML:

<a href="#">Click me!</a>

<span id="target"></span>

This is my JS:

$(document).ready(function() {


function foo(){

    writeNumber = $("#target");
    
    setTimeout(writeNumber.html("1"),1000);
    setTimeout(writeNumber.html("2"),1000);
    setTimeout(writeNumber.html("3"),1000);
    };

$('a').click(function() {
 foo();
});

});

Upvotes: 22

Views: 70345

Answers (7)

Achal Saraiya
Achal Saraiya

Reputation: 409

There is a provision to pass arguments to the function. In your case, you can do it by

setTimeout(writeNumber.html,1000,1);
setTimeout(writeNumber.html,1000,2);
setTimeout(writeNumber.html,1000,3);

third argument to setTimeout function will be pass to writeNumber.html function

Upvotes: 2

Bart
Bart

Reputation: 5203

I landed on this question. It has been answered adequately, and I think using setInterval as @Purag suggested is probably the best approach to get the desired functional behaviour. However the initial code example did not take JavaScript's asynchroneous behaviour into account. This is an often occurring error, which I've made myself on more than occasion :).

So as a side note I wanted to give another possible solution for this which mimics the initial attempt, but this time DOES consider Javascript's Asynchronousity:

setTimeout(function() {
    writeNumber.html("1");
    setTimeout(function() {
        writeNumber.html("1");
        setTimeout(function() {
            writeNumber.html("1");
        }, 1000);
    }, 1000);
}, 1000);

Now ofcourse this is clearly terrible code!

I have given a working JSFiddle of it in my own SO question. This code exemplifies the so-called pyramid of doom. And this can be mitigated by using JavaScript promises, as shown in the answers to my question. It takes some work to write a version of WriteNumber() that uses Promises, but then the code can be rewritten to somehting like:

writeNumAsync(0)
    .then(writeNumAsync)
    .then(writeNumAsync)
    .then(writeNumAsync);

Upvotes: 0

Irvin Dominin
Irvin Dominin

Reputation: 30993

You need tot use a functions to be called after the timeout is passed; you can use anonymous function too, then your function foo will look like this:

function foo(){

writeNumber = $("#target");

setTimeout(function() { writeNumber.html("1"); },1000);
setTimeout(function() { writeNumber.html("2"); },1000);
setTimeout(function() { writeNumber.html("3"); },1000);

};

Upvotes: 2

ipr101
ipr101

Reputation: 24236

You need to wrap your statements in anonymous functions and also stagger your timings -

setTimeout(function(){writeNumber.html("1")},1000);
setTimeout(function(){writeNumber.html("2")},2000);
setTimeout(function(){writeNumber.html("3")},3000);

If you set everything to 1000 the steps will pretty much run simultaneously as the setTimeout function will run the task 1 second after you called the function not 1 second after the previous call to the setTimeout function finished.

Demo - http://jsfiddle.net/JSe3H/1/

Upvotes: 7

Purag
Purag

Reputation: 17071

Just use setInterval(). Here's what I came up with. Here's your new javascript:

function foo(){
    writeNumber = $("#target");
    number      = 0;
    writeNumber.html(number);
    setInterval(function(){
        number = number+1;
        writeNumber.html(number);
    },1000);
    };
$('a').click(function() {
 foo();
});

Upvotes: 1

James Allardice
James Allardice

Reputation: 166041

setTimeout takes a function as an argument. You're executing the function and passing the result into setTimeout (so the function is executed straight away). You can use anonymous functions, for example:

setTimeout(function() {
    writeNumber.html("1");
}, 1000);

Note that the same is true of setInterval.

Upvotes: 54

tvanfosson
tvanfosson

Reputation: 532695

You need to use a function reference to be invoked later when the timer expires. Wrap each statement in an anonymous function so that it isn't executed immediately, but rather when the timer expires.

setTimeout(function() { writeNumber.html("1"); },1000);

Also, you want to use a different delay value for each one so that the timers don't expire at the same time. See an updated fiddle at http://jsfiddle.net/RqCqM/

Upvotes: 3

Related Questions