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 を表示し、次に別の要素 b0 の幅を 200ms の時間間隔で 1% 刻みで設定し、t1、b1、t2、b2 などと続けます。

しかし、200 ミリ秒の遅延はなく、コード全体がすぐに実行されます。

- - 編集 - -

うまく説明できませんでしたが、やりたいことは次のとおりです。

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

2 つの問題:

  • タイムアウト関数は間違ったiとのj値を認識します
  • それらはすべて同時に実行されます (200ms 後)

タイムアウト関数は間違ったiとのj値を認識します

主な問題は、関数が作成されたときの変数のコピーではなく、変数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+'%';};
}

呼び出すと 、 andmakeHandlerを閉じる関数が返されますが、これは変更されません。または、使い終わったときに maker 関数を破棄できるようにする上記の改良:ivaluejvalue

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#bindFunction#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グローバル オブジェクトを参照することを意味します — これが厳密モードコードでない限り、実際には になります)。windowthisundefined

それらはすべて同時に実行されます (200ms 後)

すべての関数は、上記のコードの 200 ミリ秒後に実行されるようにスケジュールされています。そして、彼らはそうするでしょう。:-) 間隔を空けたい場合は、 への呼び出しごとに 200 ミリ秒を増やしますsetTimeoutiとを掛けるだけjです。

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

最初のものは 200 ミリ秒後に実行され、2 番目のものは 200 ミリ秒後に実行されます。全体が完了するまでに約 1 分かかります。これは、6 つすべてが互いに並行して成長するのではなく、最初の要素が成長し、次に次の要素が成長することを前提としています。

または、各関数がその後続関数を呼び出すようにすることもできます。それはおそらく私がすることです。したがって、300 の関数呼び出しをスケジュールするのではなく、1 つの関数呼び出しをスケジュールし、それが発生したときに次をスケジュールします。

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

これは、 and を閉じていないためです:ij

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

これは、2 秒 (2000 ミリ秒) 後にアラートを表示することを意味します。

于 2015-08-24T08:35:12.640 に答える