0

只是似乎无法弄清楚这一点,如何防止外部循环继续执行,直到使用 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 可见,然后以 200ms 的时间间隔以 1% 的步长设置另一个元素 b0 的宽度,然后继续 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
4

3 回答 3

4

两个问题:

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

超时函数看到错误ij

主要问题是您传入的函数具有对and变量setTimeout的持久引用,而不是在创建函数时它们的副本。这意味着所有函数都将在运行时看到 和 的值,分别是和。这称为“关闭”这些变量(并且该函数称为“关闭”)。ijij650

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

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, 它返回给我们一个关闭ivalueand的函数jvalue,它不会改变。或者对上面的改进,让我们在完成后处理 maker 函数:

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 shim),那么您可以使用新的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+'%';
}

第一个参数bindthis函数中应该包含的内容;在我们的例子中我们不在乎,所以我已经指定undefined(这意味着函数将this引用全局对象——window在浏览器上——除非这是严格模式代码,在这种情况下this实际上是undefined)。

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

您的所有功能都计划在上述代码后 200 毫秒运行。他们会的。:-) 如果你想把它们分开,你需要为每次调用增加 200ms 到setTimeout. 我们可以乘以iand j

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

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

更多探索:

于 2012-05-15T03:49:06.800 回答
3

这是因为您没有关闭iand j

setTimeout(function(i, j) { 
    return function() {
        $('b'+i).style.width = j+'%';
    }
}(i, j), 200);
于 2012-05-15T03:50:33.033 回答
0
setTimeout(function () { alert('Refreshing…'); }, 2000);

这意味着在两秒(2000 毫秒)后,显示警报

于 2015-08-24T08:35:12.640 回答