setTimeout延迟不等待超时时间

22

我正在努力理解 setTimeout,但无法使其正常工作。

我在这里设置了一个示例:http://jsfiddle.net/timkl/Fca2n/

我想要在单击锚点后进行文本倒计时 - 但是即使我将延迟设置为1秒,我的 setTimeout 似乎也会立即触发。

这是我的 HTML 代码:

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

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

这是我的 JavaScript 代码:

$(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();
});

});
7个回答

54

setTimeout的参数是一个函数。你正在执行这个函数并将结果传递给setTimeout(因此该函数立即执行)。你可以使用匿名函数,例如:

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

注意 setInterval 的情况也是同样的。


谢谢你的帮助!我已经更新了我的jsfiddle:http://jsfiddle.net/timkl/cRDQh/我仍然得到相同的结果,setTimeouts在同一时间触发。 - timkl
1
没问题 :) 如果你不想让它们同时触发,可以改变超时时间长度。例如,第一个超时时间为1000毫秒,第二个为2000毫秒,以此类推。 - James Allardice
这不仅适用于 setTimeoutsetInterval,而且对于每种情况,你都应该将回调函数作为其中一个参数传递。 - Tadeck
@Tadeck - 是的,说得好。除非执行函数返回一个函数 :) - James Allardice
@JamesAllardice:同意,但在这种情况下,您仍然传递了一个回调函数(即使它是另一个函数执行的结果)。这变得更加复杂,但关键是传递一个函数,我们希望在指定时间段(或给定间隔)后执行它,而不是在传递时立即执行它。 - Tadeck

7

您需要将语句包装在匿名函数中,并且还需要分阶段设置时间 -

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

如果你把所有的步骤都设置为 1000,那么这些步骤将几乎同时运行,因为 setTimeout 函数会在调用函数后的1秒运行任务,而不是在上一次调用 setTimeout 函数完成后的1秒运行。
演示 - http://jsfiddle.net/JSe3H/1/

+1 很好的观点 - 你不仅指出了回调函数是如何传递的,还指出了它何时被执行。 - Tadeck

3

您需要使用函数引用,以便在计时器到期时调用它。将每个语句包装在匿名函数中,以便它不会立即执行,而是在计时器到期时执行。

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

此外,您希望为每个计时器使用不同的延迟值,以便计时器不会同时到期。请查看更新后的fiddle示例:http://jsfiddle.net/RqCqM/

2

在超时后需要调用一个函数,你可以使用匿名函数,那么你的函数foo将如下所示:

function foo(){

writeNumber = $("#target");

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

};

2

函数可以传递参数。在您的情况下,您可以通过以下方式实现:

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

setTimeout函数的第三个参数将传递给writeNumber.html函数。


1

只需使用setInterval()这里是我想出来的。这是你的新JavaScript代码:

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

0

我看到了这个问题。它已经得到了充分的回答,我认为使用@Purag建议的setInterval可能是获得所需功能行为的最佳方法。然而,初始代码示例没有考虑JavaScript的异步行为。这是一个经常发生的错误,我自己也犯过不止一次:)。

因此,作为一个附注,我想提供另一种可能的解决方案,模仿初始尝试,但这次考虑了JavaScript的异步性:

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

当然,这是非常糟糕的代码!

我在我的SO问题中提供了一个可工作的JSFiddle。这段代码展示了所谓的嵌套地狱。使用JavaScript promises可以缓解这个问题,正如我的问题的答案所示。编写一个使用Promises的WriteNumber()版本需要一些工作,但然后代码可以重写为类似以下内容:

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

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接