JavaScript の setTimeout と setInterval は、他人の感情を欺きやすい 2 つのメソッドです。なぜなら、呼び出されると、たとえば、
setInterval( callbackFunction , 200); ;
コードの最初の行は無限ループに入りますが、すぐに 2 行目と 3 行目が期待したものではなく、アラートの挨拶が表示されず、callbacKFunction からのニュースがないことがわかります。
現時点では、あなたはこの状況を受け入れるのが難しいです。長年確立されてきた認識を変えて新しいアイデアを受け入れるプロセスは苦痛を伴うものですが、事実は目の前にあり、JavaScript の真実を探求する必要があります。それは痛みのせいではありません。そして、JavaScript スレッドとタイマーの探索の旅を始めましょう。
雲を開けて月明かりを見てみましょう
上記の誤解の主な理由は次のとおりです。 : 無意識のうちに、JavaScript エンジンは複数のスレッドを実行しており、JavaScript タイマー コールバック関数は非同期で実行されていると信じています。
実際、JavaScript はほとんどの場合、目を欺くためにバックライトを使用する必要があります。事実:
JavaScript エンジンは 1 つのスレッドで実行されます。ブラウザでは JavaScript プログラムを実行するスレッドは常に 1 つだけです。
JavaScript エンジンが 1 つのスレッドで実行されることも意味があります。シングルスレッド シングルスレッド スレッド同期の複雑な問題については心配する必要はありません。問題は単純化されています。
では、シングルスレッドの JavaScript エンジンはどのようにしてブラウザのカーネルと連携してこれらのタイマーを処理し、ブラウザに応答するのでしょうか。
以下はブラウザと組み合わせたものです。
ブラウザのカーネル実装により、複数のスレッドがカーネル制御の下で相互に連携して同期を維持できます。ブラウザ カーネル実装に少なくとも 3 つの常駐スレッド (JavaScript エンジン スレッド、インターフェイス レンダリング スレッド、ブラウザ イベント トリガー スレッド) がある場合、これらの非同期スレッドなど、実行後に終了するスレッドもいくつかあります。以下は、スレッドの JavaScript エンジンが他のスレッドとどのように相互作用し、通信するかを示す図です。ただし、呼び出しの原則は似ています。
図からわかるように、ブラウザーの JavaScript エンジンはイベント駆動型です。ここでのイベントは、ブラウザーによって割り当てられたさまざまなタスクとみなすことができます。これらのタスクは、現在実行されているコード ブロックから発生する可能性があります。 setTimeout を呼び出してタスクを追加するなど、JavaScript エンジンを使用したり、インターフェイス要素のマウス クリック イベント、スケジュールされたトリガー時刻の到着通知、非同期リクエスト ステータス変更通知など、ブラウザ カーネルからの他のスレッドを使用したりできます。 コードの観点から見ると、タスク エンティティはさまざまなコールバック関数であり、JavaScript エンジンはタスク キューにタスクが到着するのを待っています。単一スレッドの関係のため、これらのタスクはキューに入れられ、エンジンによって次々に処理される必要があります。上の図の 🎜>T1-t2..tn はさまざまな時点を表し、tn の下の対応する小さな四角は、ある時点のタスクについて、時刻 t1 であると仮定すると、エンジンは対応するタスク ブロック コードで実行されます。この時点で、ブラウザ カーネル内の他のスレッドのステータスを説明します。
T1 時間:
GUI レンダリング スレッド:
このスレッドは、ブラウザ インターフェイスの HTML 要素をレンダリングする役割を果たします。インターフェイスを再描画する必要がある場合 (再描画)、または何らかの操作によってリフローが発生した場合に、このスレッドが実行されます。ただし、この記事では JavaScript について説明します。タイミング メカニズムについては、この時点でレンダリング スレッドについて説明する必要があります。このスレッドと JavaScript エンジン スレッドは相互に排他的であるためです。これは、JavaScript スクリプトがこれらの要素の属性を変更するときに操作できるため、容易に理解できます。インターフェイスを同時にレンダリングすると、レンダリング スレッドの前後で取得された要素データが不一致になる可能性があります。
JavaScript エンジンがスクリプトを実行している間、ブラウザのレンダリング スレッドは一時停止状態になります。 したがって、ノードの追加、ノードの削除、ノードの外観の変更など、スクリプト内で実行されたインターフェースの更新は、すぐには反映されません。キューにある場合、JavaScript エンジンはアイドル状態のときにレンダリングする機会があります。JS コード ブロックのアラートが発生すると、実際には JS が実行されているのではなく、なぜインターフェイスが更新されるのかという疑問があるかもしれません。
GUI イベント トリガー スレッド:
JavaScript スクリプトの実行は行われません。図からわかるように、t1 期間中にユーザーがマウス ボタンをクリックすると、そのクリックがブラウザのイベント トリガー スレッドによってキャプチャされ、マウス クリック イベントが形成されます。 JavaScript エンジン スレッドでは、このイベントは他のスレッドからタスクに非同期で送信されます。エンジンは t1 でタスクを処理しているため、このマウス クリック イベントは処理を待機しています。 🎜>タイミングトリガースレッド:
ここでのブラウザ モデルのタイミング カウンタは、JavaScript エンジンによってカウントされないことに注意してください。これは、JavaScript エンジンがブロックされたスレッド状態にある場合、時間をカウントすることができないためです。したがって、キュー内のスケジュールされたイベントも非同期イベントです。
図からわかるように、この t1 時間の間、マウス クリック イベントがトリガーされた後、この時点で、以前に設定された setTimeout タイミングも到着します。これは、JavaScript エンジンにとって非常に重要です。たとえば、スケジュールされたトリガー スレッドは、非同期のスケジュールされたイベントを生成し、クリック イベント コールバックの後にキューに入れられます。
同様に、t1 期間内に、次に特定の setInterval がスケジュールされます。間隔のタイミングにより、これら 2 つのイベントがキューに追加されました。
期間 t1 が非常に長い場合、Yuanda は setInterval タイミング間隔に基づいており、タイミング トリガー スレッドは非同期タイミング イベントを継続的に生成することがわかります。ただし、t1 と最初のタイミング イベントの前にあるタスクが処理されると、キュー内のスケジュールされたイベントは中断されることなく順番に実行されます。これは、JavaScript エンジンの場合、処理キュー内の各タスクは同じように処理されますが、
t1 以降、つまり現在処理されているタスクの処理順序が異なるためです。 JavaScript エンジンはタスク キューをチェックし、現在のキューが空ではないことを確認し、t2 で対応するタスクを取り出して実行します。この観点からは、次のようになります。
キューが空でない場合、エンジンはタスクが処理されるまでキューの先頭からタスクを取り出します。つまり、キューに戻った後、エンジンは次のタスクを実行します。その前にキュー内の他のタスクを実行することはできません。タスクは戻ります。
最初のタイマーは無限ループであり、最初のタイマーは 2 番目のタイマーよりも先に文字列を出力します。 >
setTimeout(function(){
for( ; ;);
}, 50);
setTimeout(function(){alert('Hello');}, 51);ブラウジングがない場合、サーバーがスクリプトがビジーであることを示すメッセージが表示される前に、2 番目のタイマー アラートは表示されません。単一スレッドの関係のため、最初のタイマー コールバック スクリプトが返されるまで 2 番目のスクリプトは実行されません。
私はあなたを信じます。 これで、JavaScript がマルチスレッド化できるかどうかが明らかになり、JavaScript タイマーの実行メカニズムも理解できました。いくつかのケースを分析してみましょう:
ケース 1: setTimeout と setInterval
/* コード ブロック... */ setTimeout(arguments.callee,
}, 10);
setInterval(){
/*コード ブロック... */
}, 10);
これら 2 つのコードは一緒に見えますが、実際には異なります。最初の段落のコールバック関数の setTimeout は、次の新しい setTimeout タイミングを設定します。 JavaScript エンジンは、次のコールバックが処理されるまで、2 つの setTimeout コールバックの実行時間間隔が 10 ミリ秒以上であると想定されます。 setInterval が設定された後、タイミング トリガー スレッドは 10 秒ごとに非同期タイミング イベントを継続的に生成し、それらをタスク キューの最後に配置します。理論的には、2 つの setInterval コールバック間の実行時間間隔は
ケース 2: 非同期 ajax リクエストは本当に非同期ですか?
多くのクラスメートや友人 JavaScript は単一スレッドで実行されると言われているため、XMLHttpRequest は本当に非同期なのかわかりません。 実際、リクエストは確かに非同期ですが、このリクエストは新しいスレッドリクエストを開くためにブラウザによって行われます(上の図を参照)。コールバックが以前に設定されている場合、リクエストのステータスが変化したときに、非同期スレッドはステータス変更イベントを生成し、それを JavaScript エンジンの処理キューに入れて処理を待機します。タスクが処理されると、JavaScript エンジンは常に単一スレッドでコールバック関数を実行します。具体的には、クリックすると関数セットが実行されます。単一スレッドの onreadystatechange によって。