1. ホーム
  2. ジャバスクリプト

[解決済み】forループのsetTimeoutで連続した値が表示されない【重複あり

2022-03-30 11:02:23

質問

こんなスクリプトがあります。

for (var i = 1; i <= 2; i++) {
    setTimeout(function() { alert(i) }, 100);
}

しかし 3 は2回ともアラートされ、代わりに 1 では 2 .

を渡す方法はありますか? i 文字列として関数を書かずに?

どのように解決するのですか?

タイムアウト機能ごとに、"i"の別個のコピーが存在するように手配する必要があります。

function doSetTimeout(i) {
  setTimeout(function() { alert(i); }, 100);
}

for (var i = 1; i <= 2; ++i)
  doSetTimeout(i);

もしこのようなことをしなければ(この同じアイデアの他のバリエーションもあります)、各タイマーハンドラ関数が 共有 は、同じ変数 "i"です。 ループが終了したとき、"i"の値はどうなっているでしょうか? それは3です! 中間的な関数である コピー の値が作成されます。タイムアウトハンドラはそのコピーのコンテキストで作成されるので、使用するプライベートな "i" を持ちます。

編集 - いくつかのタイムアウトを設定すると、すべてのハンドラが同時に起動するという事実に対して、いくつかの混乱が見られるというコメントがありました。重要なのは、このような処理は 設定 タイマーの呼び出し、つまり setTimeout() - はほとんど時間がかかりません。つまり、システムに "1000ミリ秒後にこの関数を呼び出してください" と伝えると、タイマーキューにタイムアウト要求をインストールする処理が非常に速いため、ほぼ即座に返されます。

したがって、もし 継承 のように、タイムアウト要求が行われ、遅延時間の値がそれぞれ同じである場合、その時間が経過すると、すべてのタイマーハンドラが次々と呼び出されることになります。

もし、一定時間ごとにハンドラを呼び出す必要があるのであれば、以下のような方法があります。 setInterval() と全く同じように呼び出されます。 setTimeout() あるいは、タイムアウトを設定し、その時間に反復カウンタを乗じることもできます。つまり、私のサンプルコードを修正することです。

function doScaledTimeout(i) {
  setTimeout(function() {
    alert(i);
  }, i * 5000);
}

(ただし 100 ミリ秒のタイムアウトでは、効果があまり現れないので、5000に増やしました)。の値は i は基本遅延値に乗算されるので、ループ内でこれを5回呼び出すと、5秒、10秒、15秒、20秒、25秒の遅延が発生することになります。

更新

ここで2018年、よりシンプルな代替案があります。関数よりも狭いスコープで変数を宣言する新しい機能により、そのように修正すれば元のコードは動作します。

for (let i = 1; i <= 2; i++) {
    setTimeout(function() { alert(i) }, 100);
}

let 宣言は var が存在することになり、それ自体が i ループの各反復のために。