6

変数setTimeout関数を渡して、それを使って何かをしたいです。私がそれの値を警告するときi、私が予期していなかった数を私に示します。何が間違っているのですか?1から8までのログ値が必要です。

var end=8;
for (var i = 1; i < end; i ++) {
       setTimeout(function (i) {
           console.log(i);   

       }, 800);
   }
4

4 に答える 4

13

これを解決する標準的な方法は、ファクトリ関数を使用することです。

var end=8;
for (var i = 1; i < end; i ++) {
       setTimeout(makeResponder(i), 800);
   }

function makeResponder(index) {
    return function () {
        console.log(index);   
   };
}

実例| ソース

そこで、ループ内で呼び出す と、変数ではなく、makeResponder渡された引数 ( ) を閉じる関数が返されます。(これは重要です。無名関数から引数を削除しただけでは、コードは部分的に機能しますが、すべての関数は、最初にスケジュールされたときではなく、実行されたときの値を参照します;あなたの例で、'd all see .)indexiii8


更新以下のコメントから:

...そのように呼んだら正しいsetTimeout(makeResponder(i),i*800);でしょうか?

はい、最後の呼び出しよりも約 800 ミリ秒遅れて各呼び出しを発生させることが目標である場合、それは機能します。

実例| ソース

やってみsetTimeout(makeResponder(i),setInterval(i));function setInterval(index) { console.log(index*800); return index*800; }たけどうまく動かない

あなたはsetIntervalそのように使用しませんし、おそらくこれにはまったく使用したくないでしょう。


さらなる更新:あなたは以下に言いました:

最初の繰り返し印刷 8 遅延 8 秒、2 回目の繰り返し印刷 7 遅延 7 秒 ........印刷 2 遅延 2 秒 ...印刷 0 遅延 0 秒が必要です。

2 番目のタイムアウトを使用して、上記の原則を再度適用するだけです。

var end=8;
for (var i = 1; i < end; i ++) {
       setTimeout(makeResponder(i), i * 800);
   }

function makeResponder(index) {
    return function () {
        var thisStart = new Date();
        console.log("index = " + index + ", first function triggered");
        setTimeout(function() {
            console.log("index = " +
                        index +
                        ", second function triggered after a further " +
                        (new Date() - thisStart) +
                        "ms delay");
        }, index * 1000);
   };
}

実例| ソース

これで、これを進めるために必要なすべてのツールが揃ったと思います。

于 2012-04-18T10:37:33.737 に答える
3

あなたの問題は、しばらくして関数が起動しiたときに変数を参照していて、それまでに の値が変更されていることです(ループの最後に移動しています。各 setTimeout を適切な値の に保つには、キャプチャする必要がありますコールバックごとに個別にその値。 setTimeout()iforiisetTimeout()

ファクトリ関数を使用した前の回答はそれで問題ありませんが、自己実行関数はファクトリ関数よりも入力して追跡するのが少し簡単だと思いますが、両方ともクロージャで必要な変数をキャプチャして静的値を参照できるため、両方とも機能します。 setTimeout コールバックで。

この問題を解決するために自己実行関数がどのように機能するかを次に示します。

var end=8;
for (var i = 1; i < end; i ++) {
       (function (index) {
           setTimeout(function() {
               console.log(index);
           }, 800);
       })(i);
   }

の値に比例してタイムアウト遅延を設定するには、次のiようにします。

var end=8;
for (var i = 1; i < end; i ++) {
    (function (index) {
        setTimeout(function() {
            console.log(index);
        }, index * 800);
    })(i);
}

自己実行関数には の値が渡され、iその値を含むその関数内の引数には名前が付けindexられるため、参照しindexて適切な値を使用できます。


ES6 で let を使用する

Javascript の ES6 (2015 年にリリース) では、ループ内で使用できlet、ループforの反復ごとに新しい個別の変数が作成されforます。これは、次のような問題を解決するためのより「現代的な」方法です。

const end = 8;
for (let i = 1; i < end; i++) {            // use "let" in this line
     setTimeout(function() {
         console.log(i);
     }, 800);
 }
于 2012-04-18T10:59:25.963 に答える
0

これが機能しない主な理由は、 のsetTimeout後に実行するように設定されている800と のスコープのためですi

実行するまでに、 which の値iはすでに変更されています。したがって、決定的な結果は得られませんでした。TJ が言ったように、これを回避する方法はハンドラー関数を使用することです。

function handler( var1) {
    return function() {
      console.log(var1);  
    }        
}

var end = 8;
for (var i = 1; i < end; i++) {     
   setTimeout(handler(i), 800);
}

デモ

于 2012-04-18T10:46:31.410 に答える
-2

setTimeout accepts variables as additional arguments:

setTimeout(function(a, b, c) {
    console.log(a, b, c);
  }, 1000, 'a', 'b', 'c');

Source.

EDIT: In your example, the effective value of i will likely be 8, since the function is merely to be called after the loop has finished. You need to pass the current value of i for each call:

var end=8;
for (var i = 1; i < end; i ++) {
  setTimeout(function (i) {
      console.log(i);   
   }, 800, i);
}
于 2012-04-18T10:33:22.420 に答える