関数を呼び出すforEachがあります。呼び出されるたびに遅延が必要です。 forEach内のsetTimeout内に配置しました。最初の待機後のタイムアウトは考慮されません。代わりに、一度待機してから、すべてを一度に実行します。タイムアウトを5秒に設定し、コンソールを使用して確認しています。 5秒間待機してから、いくつかのfoobarコンソールが一度にすべてログに記録します。
なぜこの動作が発生するのですか?
var index = 0;
json.objects.forEach(function(obj) {
setTimeout(function(){
console.log('foobar');
self.insertDesignJsonObject(obj, index);
}, 5000);
});
Jasonが言ったことは彼の答えでは完全に正しいが、私はそれをより明確にするために試してみると思った。
これは実際には古典的な閉鎖の問題です。通常、次のようになります。
for(var i = 0; i < 10; i++){
setTimeout(function(){
console.log(i);
},i * 1000)
}
初心者はコンソールに以下が表示されることを期待します。
0
(0 seconds pass...)
1
(1 second passes...)
2
(etc..)
しかし、そうではありません!実際に表示されるのは番号10
10回記録されます(1秒間に1回)!
「なぜそれが起こるのですか?」いい質問ですね。閉鎖範囲。上記のfor
ループにはクロージャスコープがありません。なぜならJavaScriptでは、関数(ラムダ)だけがクロージャスコープを持つからです!
参照: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Closures
しかしながら!これを試みた場合、あなたの試みは望ましい出力を達成したでしょう:
json.objects.forEach(function(obj,index,collection) {
setTimeout(function(){
console.log('foobar');
self.insertDesignJsonObject(obj, index);
}, index * 5000);
});
"closur-ed" index
変数にアクセスできるため、関数(ラムダ)が呼び出されたときに、その状態が期待される状態であることに依存できます。
その他のリソース:
http://javascript.info/tutorial/closures
http://code.tutsplus.com/tutorials/closures-front-to-back--net-24869
setTimeout はayncです。それが行うことは、registerコールバック関数であり、遅延後にトリガーされるバックグラウンドに配置します。 forEachは同期関数です。あなたのコードは、コールバックを「一度に」登録し、それぞれが5秒後にトリガーされるようにしました。
それを避ける2つの方法:
タイマーを設定するためのインデックスがあります。
json.objects.forEach(function(obj, index) {
setTimeout(function(){
// do whatever
}, 5000 * (index + 1));
});
このように、遅延係数はオブジェクトのインデックスに基づいているため、同時に登録した場合でも、独自の遅延に基づいてトリガーされます。 index + 1
は0から始まるため、問題の結果と同じ結果を保持します。
setIntervalでオブジェクトをループします
var i = 0;
var interval = setInterval(function(){
var obj = json.objects[i];
// do whatever
i++;
if(i === json.objects.length) clearInterval(interval);
}, 5000);
setInterval はsetTimeoutに似ていますが、間隔に基づいて定期的にトリガーされます。ここでは、オブジェクトにアクセスし、間隔関数のインデックスinsideを更新します。また、最後に間隔をクリアすることを忘れないでください。
これらの2つの違いは、setIntervalが1つの関数のみを登録し、リスト内のアイテムの数だけ登録されたsetTimeoutと比較されることです。