javascript - JS超时不触发

标签 javascript timer

似乎无法弄清楚这一点,如何防止外循环继续执行,直到使用 setTimeout 执行内循环:

    function $(s) { return document.getElementById(s); }
    var i = 0;
    for (i=0; i<6; i++){
        $('t'+i).className = 'show';
        cl('t'+i);
        for (var j=0; j<50; j++){
            cl('b'+i+'='+j+'%');
            setTimeout(function(){ $('b'+i).style.width = j+'%';},200);
        }
    }

这段代码应该首先使元素 t0 可见,然后以 1% 的步长设置另一个元素 b0 的宽度,时间间隔为 200ms,然后继续 t1,b1, t2,b2 等.

但是没有 200ms 的延迟,整个代码立即执行。

--- 编辑 ---

我没有解释得很好,这就是我想做的:

1. show element Ax
2. increase element Bx width by 1% every 200ms until 50%
3. wait until element Bx reaches 50% before continuing
4. increment x
5. goto 1

最佳答案

两个问题:

  • 超时函数看到错误ij值(value)观
  • 它们同时运行(200 毫秒后)

超时函数看到错误ij

主要问题是您传递给 setTimeout 的函数对i有一个持久的引用和j变量,不是它们在创建函数时的副本。这意味着所有函数都会看到 i 的值。和j当它们运行时,这将是 650分别。这称为“封闭”这些变量(该函数称为“封闭”)。

解决此问题的常用方法是创建函数,使其关闭不发生更改的内容。有几种方法可以做到这一点;我最喜欢的是使用工厂函数:

function $(s) { return document.getElementById(s); }
var i = 0;
for (i=0; i<6; i++){
    $('t'+i).className = 'show';
    cl('t'+i);
    for (var j=0; j<50; j++){
        cl('b'+i+'='+j+'%');
        setTimeout(makeHandler(i, j), 200);
    }
}
function makeHandler(ivalue, jvalue) {
    return function(){ $('b'+ivalue).style.width = jvalue+'%';};
}

现在我们调用makeHandler ,它返回给我们一个关闭 ivalue 的函数和jvalue ,这不会改变。或者对上面的内容进行改进,让我们在完成后可以处理生成器函数:

function $(s) { return document.getElementById(s); }
var i = 0;
var makeHandler = function(ivalue, jvalue) {
    return function(){ $('b'+ivalue).style.width = jvalue+'%';};
};
for (i=0; i<6; i++){
    $('t'+i).className = 'show';
    cl('t'+i);
    for (var j=0; j<50; j++){
        cl('b'+i+'='+j+'%');
        setTimeout(makeHandler(i, j), 200);
    }
}
makeHandler = undefined;

如果您可以依赖 ES5 功能(因为您的目标环境,或者因为您已包含 ES5 垫片),则可以使用新的 Function#bind 获得大致相同的效果。 。 Function#bind创建一个新函数,就像 makeHandler确实如此,但引擎总有可能进行一些优化:

function $(s) { return document.getElementById(s); }
var i = 0;
for (i=0; i<6; i++){
    $('t'+i).className = 'show';
    cl('t'+i);
    for (var j=0; j<50; j++){
        cl('b'+i+'='+j+'%');
        setTimeout(handler.bind(undefined, i, j), 200);
    }
}
function handler(){
    $('b'+ivalue).style.width = jvalue+'%';
}

bind 的第一个参数是什么this应该在函数中;在我们的例子中,我们不在乎,所以我指定了 undefined (这意味着该函数将获得 this 引用全局对象 - window ,在浏览器上 - 除非这是严格模式代码,在这种情况下 this 实际上将是 undefined )。

它们都同时运行(200毫秒后)

您的所有函数都计划在上述代码之后 200 毫秒运行。他们也会这么做的。 :-) 如果您想将它们间隔开,您需要将每次调用 setTimeout 的时间增加 200 毫秒。 。我们可以乘以 ij :

setTimeout(makeHandler(i, j), (200 * i * j) + 200);

现在第一个将在 200 毫秒后运行,第二个将在 200 毫秒后运行,依此类推。整个过程大约需要一分钟才能完成。这假设您希望第一个元素增长,然后是下一个,然后是下一个,而不是所有六个元素彼此并行增长。

或者,您可能希望每个函数都调用其后继函数。这可能就是我会做的。因此,与其调度 300 个函数调用,而只需调度一个函数调用,当它发生时,调度下一个:

function $(s) { return document.getElementById(s); }

// Our counters are here
var i = 0, j = 0;

// This handles the outer portion of the loop (mostly)
function outer() {
    $('t'+i).className = 'show';
    cl('t'+i);
    j = 0;
    // Schedule the first inner portion 200ms from now
    setTimeout(inner, 200);
}

// This handles the inner portion of the loop (mostly)
function inner() {
    // Do this bit
    $('b'+i).style.width = j+'%';
    ++j;
    if (j < 50) {
       // Continue the inner loop in 200ms
       setTimeout(inner, 200);
    }
    else {
       // Set up next outer loop
       j = 0;
       ++i;
       if (i < 6) {
           // Not done yet, keep going
           setTimeout(outer, 200);
       }
     }
}

// Kick it off
setTimeout(outer, 200);

在上面,我还移动了这些行:

$('t'+i).className = 'show';
cl('t'+i);

...进入延迟代码,我怀疑这是合适的。

更多探索:

关于javascript - JS超时不触发,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/10593888/

相关文章:

java - 30 秒后停止线程并重试

javascript - 编程定时器

javascript - 多系列折线图

javascript - 无法从本地主机向远程服务器发出 ajax 请求

javascript - nodejs - 仅当来自服务器 ip 时才允许请求

java - 如何用JLabel创建定时器?

java - Android 秒表代码更有效的方法?

android - 在警报对话框中显示倒数计时器

javascript - 如何获取我的GET值?

javascript - 如何考虑当旁边的用户名太长时自动加宽搜索字段的脚本