web-dev-qa-db-ja.com

setTimeoutがループをキャンセルしないのはなぜですか?

JavaScript whileステートメント(Chromeのコンソール内)が1ミリ秒で変数をインクリメントできるのは何回か疑問に思ったので、このスニペットをコンソールに直接書き込みました。

var run = true, i = 0;
setTimeout(function(){ run = false; }, 1);
while(run){ i++; }

問題は、それが永久に実行されることです。
なぜこれが起こっているのですか、どうすれば解決できますか?

75
rev

これは、JavaScriptのワンスレッドの性質に戻ります1。何が起こるかはほとんどこれです:

  1. 変数が割り当てられます。
  2. 関数をスケジュールして、_run = false_を設定します。これは実行されるようにスケジュールされていますafter現在の関数が実行されます(または他の現在アクティブなものは何でも)。
  3. あなたはあなたの無限のループを持っていて、現在の関数の中にとどまります。
  4. 無限ループ(never)の後、setTimeout()コールバックが実行され、_run=false_が実行されます。

ご覧のとおり、ここではsetTimeout()アプローチは機能しません。 while条件で時間をチェックすることでこれを回避できますが、これは実際の測定値を改ざんします。

1 少なくともより実用的な目的では、シングルスレッドとして見ることができます。実際には、いわゆる「イベントループ」があります。そのループでは、すべての関数が実行されるまでキューに入れられます。新しい関数をキューに入れると、そのキュー内のそれぞれの位置に配置されます。 現在の関数が終了した後、エンジンはキューから次の関数を取得します(たとえば、setTimeout()によって導入されたタイミングに関して、それを実行します。
その結果、すべての時点で1つの関数のみが実行されるため、実行はほぼシングルスレッドになります。以下のリンクで説明されているイベントにはいくつかの例外があります。


参考のために:

80
Sirko

JavaScriptはシングルスレッドであるため、ループ内にいる間は他に何も実行されません。

24
laurent

速度を計算する時間を絶えず取得することなく、Chromeの真の速度を維持するには、次のJSコードを試すことができます。

var start = new Date().getTime()
var i = 0
while(i<1000000)
{
    i++
}
var end = new Date().getTime()
var delta = end-start
var speed = i/delta
console.log(speed + " loops per millisecond")
19
SyntaxLAMP

Javascriptはシングルスレッドです。つまり、一度に1つの命令のみを順番に実行します。

イベントシステムは、他の多くの言語やライブラリと同様に、イベントループによって処理されます。イベントループは基本的にループであり、反復ごとにキュー内のメッセージをチェックし、イベントをディスパッチします。

Javascriptでは(このパターンを実装する言語のmotのように)、スタックが空のとき、つまり、すべての関数がプログラムコードの最後に戻り値を持っているときに、イベントループが呼び出されます。

あなたの「本当の」プログラムは舞台裏で次のように見えます:

var run = true, i = 0;
setTimeout(function(){ run = false; }, 1);
while(run){ i++; }

while(true) {
/*
 * check for new messages in the queue and dispatch
 * events if there are some
 */
  processEvents();
}

したがって、タイムアウトが終了したことを示す時計からのメッセージは処理されません。

イベントループの詳細: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/EventLoop


もちろん、もう少し複雑です。ここでそれらの例を確認してください: JavaScriptはシングルスレッドであることが保証されていますか?tl; dr:一部のブラウザエンジンでは、一部の外部イベントはイベントループに依存せず、発生するとすぐに発生し、現在のタスクをプリエンプトします。ただし、これはsetTimeoutの場合ではなく、キューにメッセージを追加するだけで、決してすぐに発砲します。)

4
jillro

WhileループはsetTimeoutにアクセスしません。セットがtrueで実行されるコードがあり、それがfalseになることはありません。

2
ChriskOlson

JavaScriptにはシングルスレッドがあり、どこにでもシングルスレッドがあります。

この質問は良いと思います: JavaScriptはシングルスレッドであることが保証されていますか?

コードがループ内にある場合、他のocdeは実行およびブロックされません。

1
user1968030