基本レベルから、JavaScript タイマーがどのように機能するかを理解することが非常に重要です。タイマーの実行は、多くの場合、私たちの直感的な想像とは異なります。これは、JavaScript エンジンがシングルスレッドであるためです。まず、次の 3 つの関数がどのようにタイマーを制御するかを理解しましょう。
var id = setTimeout(fn, late); - タイマーを初期化し、指定された時間間隔の後に実行します。この関数は、タイマーをキャンセルするために使用できる一意のフラグ ID (数値タイプ) を返します。
var id = setInterval(fn, late); - setTimeout に似ていますが、関数がキャンセルされるまで継続的に関数を呼び出します (時間間隔は遅延パラメーターです)。
clearInterval(id);、clearTimeout(id); - タイマー ID (setTimeout と setInterval の戻り値) を使用してタイマー コールバックの発生をキャンセルします
タイマーの内部動作を理解するために、議論する必要がある重要な概念があります。それは、タイマーの遅延は保証されていないということです。すべての JavaScript コードはスレッド内で実行されるため、すべての非同期イベント (マウス クリックやタイマーなど) は、実行する機会がある場合にのみ実行されます。
この図には理解すべき情報がたくさんあります。それを完全に理解すれば、JavaScript エンジンが非同期イベントを実装する方法をよく理解できるようになります。これは 1 次元のアイコンです。垂直方向は時間を表し、青いブロックは JavaScript コードの実行ブロックを表します。たとえば、最初の JavaScript コード実行ブロックには約 18 ミリ秒かかり、マウスのクリックによってトリガーされるコード実行ブロックには 11 ミリ秒かかります。
JavaScript エンジンは一度に 1 つのコードしか実行しないため (これは JavaScript のシングルスレッドの性質によるものです)、各 JavaScript コード実行ブロックは他の非同期イベントの実行を「ブロック」します。これは、非同期イベント (マウスのクリック、タイマーのトリガー、Ajax 非同期リクエストなど) が発生すると、これらのイベントのコールバック関数が実行キューの最後にキューに入れられ、実行を待機することを意味します (実際には、 、キューイング方法はブラウザによって異なります。デバイスによって異なるため、これは単なる簡略化です);
最初の JavaScript 実行ブロックから学習を開始します。このブロックでは、10 ミリ秒の setTimeout() と 10 ミリ秒の setInterval() という 2 つのタイマーが初期化されます。 タイマーが初期化される時期と場所に応じて (タイマーの初期化後にカウントが開始されます)、タイマーは実際には最初のコード ブロックの実行が完了する前にトリガーされます。ただし、タイマーにバインドされた関数はすぐには実行されません (すぐに実行されない理由は、JavaScript がシングルスレッドであるためです)。実際、遅延された関数は実行キューの最後にキューに入れられ、次に実行される適切なタイミングを待ちます。さらに、最初の JavaScript 実行ブロックでは、「マウス クリック」イベントが発生していることがわかります。 JavaScript コールバック関数はこの非同期イベントにバインドされています (ユーザーがこの (クリック) イベントをいつ実行するかはわかりません。そのため、この関数は上記のタイマーのようにすぐには実行されず、キューに入れられます)。実行キューの最後で、次の適切な時点での実行を待機します。
最初の JavaScript 実行ブロックが実行されると、ブラウザーはすぐに「どの関数 (ステートメント) が実行を待っていますか?」と質問します。このとき、「マウスクリックイベントハンドラ関数」と「タイマーコールバック関数」が実行待ちになっています。ブラウザは 1 つを選択し (実際には「マウス クリック イベントのハンドラー関数」を選択します。図から、最初にキューに入れられていることがわかります)、すぐに実行されます。 「タイマー コールバック関数」は、次の適切な時間が実行されるまで待機します。
「マウス クリック イベント ハンドラー関数」が実行されると、初めて setInterval コールバック関数がトリガーされることに注意してください。 setTimeout コールバック関数と同様に、実行キューの最後にキューに入れられ、実行を待ちます。ただし、必ず注意してください。setInterval コールバック関数が 2 回目にトリガーされると (この時点では setTimeout 関数はまだ実行中です)、setInterval の最初のトリガーは破棄されます。長いコード ブロックが実行されると、すべての setInterval コールバック関数が実行キューの最後尾にキューイングされる可能性があり、コード ブロックが実行された後、これらの関数の間に一連の setInterval コールバック関数が実行を待機することになります。すべて完了するまでインターバルはありません。したがって、ブラウザーは、キュー内に間隔ハンドラーがなくなると、次のハンドラーをキューの最後に入れる傾向があります (これは間隔の問題によるものです)。
3 番目の setInterval コールバック関数がトリガーされたとき、前の setInterval コールバック関数がまだ実行されていることがわかります。これは非常に重要な事実を示しています。setInterval は現在実行されているものを考慮せず、ブロックされているすべての関数をキューの最後にキューに入れます。これは、2 つの setInterval コールバック関数間の時間間隔が犠牲になる (短縮される) ことを意味します。
最後に、2 番目の setInterval コールバック関数が実行されると、JavaScript エンジンの実行を待機しているプログラムがないことがわかります。これは、ブラウザが新しい非同期イベントの発生を待機していることを意味します。 50 ミリ秒で、新しい setInterval コールバック関数が再びトリガーされます。この時点で、その実行をブロックする実行ブロックはありません。したがって、すぐに実行されます。
setTimeout と setInterval の違いを明確にするために例を使用してみましょう。
setTimeout ( function ( ) { /* Some long block of code... */ setTimeout (arguments. callee, 10 ); }, 10 ); setInterval ( function ( ) { /* Some long block of code... */ }, 10 );
これら 2 行のコードは一見すると違いがないように見えますが、異なります。 setTimeout コールバック関数の実行と前回の実行の間の間隔は少なくとも 10 ミリ秒 (場合によってはそれ以上ですが、10 ミリ秒未満ではありません) ですが、setInterval コールバック関数は、最後の実行が完了したかどうかに関係なく、10 ミリ秒ごとに実行しようとします。
ここで多くのことを学びました。要約すると次のようになります。
JavaScript エンジンはシングルスレッドであるため、すべての非同期イベントは実行のためにキューに入れられます
非同期コードを実行する場合、setTimeout と setInterval には基本的な違いがあります
タイマーがブロックされており、すぐに実行できない場合は、次に実行可能な時点 (予想される時間間隔よりも長い) まで実行が遅延されます
setInterval コールバック関数の実行時間が十分に長い (指定された時間間隔より長い) 場合、これらの関数は時間間隔を空けずに連続して実行されます。
以上がこの記事の全内容です。JavaScript の非同期処理を学ぶ皆さんの参考になれば幸いです。