私は厄介な状況にありました、
私はほぼ3年間純粋なJavaScriptを使用しており、JavaScriptは単一スレッド言語であり、setInterval
を使用して非同期実行をシミュレートできることを知っています。 setTimeout
関数、
しかし、彼らがどのように機能するかについて考えたとき、私はそれを明確に理解することができませんでした。では、これらの関数は実行コンテキストにどのように影響するのでしょうか。
特定の時間にコードの一部のみが実行され、その後別の部分に切り替わったと思います。その場合、setInterval
またはsetTimeout
の呼び出しが多いとパフォーマンスに影響しますか?
JavaScriptはシングルスレッドですが、ブラウザはそうではありません。ブラウザには少なくとも3つのスレッドがあります。JavaScriptエンジンスレッド、UIスレッド、タイミングスレッドで、setTimeout
とsetInterval
のタイミングはタイミングスレッドによって行われます。
setTimeout
またはsetInterval
を呼び出すと、ブラウザーのタイマースレッドがカウントダウンを開始し、タイムアップするとJavaScriptスレッドの実行スタックにコールバック関数が配置されます。コールバック関数は、スタック内の他の関数が終了するまで実行されません。したがって、タイムアップ時に他の時間がかかる関数が実行されている場合、setTimeout
のコールバックは時間内に完了しません。
ブラウザには、イベントexのAPIと同じように、タイマー機能のAPIがあります。
'クリック'
'スクロール'
アプリケーションに次のコードがあると仮定します
function listener(){
...
}
setTimeout(listener, 300)
function foo(){
for(var i = 0; i < 10000; i++){
console.log(i)
}
}
foo()
この時点で、コールスタックの上に記述したコードに従って、次のようになります。
呼び出しスタック-> foo
コードですでに1つのタイムアウトを定義しており、「foo」が完了する前、つまり300ミリ秒で実行しているため、fooは実行を完了するのに1秒かかると仮定します。
それから何が起こりますか?
JavaScriptはfooの実行を停止し、setTimeoutの実行を開始しますか?
いいえ
JavaScriptはシングルスレッドであるため、先に進む前にfooの実行を完了する必要がありますが、ブラウザはfooの実行後に「setTimeout」が実行されることをどのように保証しますか?
ここでJavaScriptの魔法が登場します
300msが経過すると、ブラウザの「タイマーAPI」が起動し、タイムアウトハンドラを「メッセージキュー」に配置します
この時点で、上の画像の「メッセージキュー」は次のようになります。
メッセージキュー-> setTimout:listner
そして
呼び出しスタック-> foo
そして、「コールスタック」が空になる、つまりfooが実行を完了すると、図に示す「イベントループ」がメッセージキューからメッセージを取得し、スタックにプッシュします。
「イベントループ」の唯一の仕事は、「コールスタック」が空になり、「メッセージキュー」にエントリがある場合、「メッセージキュー」からメッセージをデキューして「コールスタック」にプッシュすることです。
この時点で、上の画像のMessage Queueは次のようになります。
メッセージキュー->
そして
呼び出しスタック->リスナー
そして、それがsetTimeoutとsetIntervalが機能する方法です。ただし、setTimeoutで300ミリ秒を指定したとしても、「foo」が実行を完了した後、つまりこの場合は1秒後に実行されます。そして、それがsetTimeout/setIntervalで指定されたタイマーが"Minimum Time"関数の実行の遅延を示す理由です。
JavaScriptはシングルスレッドですが、ブラウザはそうではありません。
関数とステートメントが実行される1つのスタックがあります。関数が実行されるようにキューに入れられているキューが1つあります。イベントテーブルのsetTimeoutとsetIntervalで定義されている特定の時間に関数を保持できるWeb APIがあります。
javaScriptエンジンがjsファイルを1行ずつ実行するとき、ステートメントまたは関数呼び出しとして行を見つけた場合、それをスタックにロードして実行しますが、それがsetTimeoutまたはsetInterval呼び出しである場合、setTimeoutまたはsetIntervalに関連付けられた関数ハンドラーがTIME APIによって取り出されます(ブラウザのWeb APIの1つ)、その間保持します。
この時間が終了すると、Time Apiはその関数を実行キューの最後に置きます。
現在、その関数の実行は、キュー内で先行している他の関数呼び出しに依存しています。
注:この関数呼び出しは、ウィンドウオブジェクトに対して呼び出されます。
setTimeout(function () {console.log(this)}, 300)
ウィンドウ{postMessage:ƒ、ぼかし:ƒ、フォーカス:ƒ、閉じる:ƒ、フレーム:ウィンドウ、…}
JavaScriptはシングルスレッドのスクリプト言語であるため、一度に1つのコードを実行できます(そのシングルスレッドの性質により)。これらの各コードブロックは、他の非同期イベントの進行を「ブロック」しています。つまり、非同期イベント(マウスクリック、タイマーの起動、XMLHttpRequestの完了など)が発生すると、後で実行するためにキューに入れられます。
setTimeout() setTimeout()を使用すると、何らかの理由により(setTimeoutの)前のイベントがブロックされた場合にのみ実行されます。 setTimeout()関数。 setTimeoutコールバック関数の実行中に、イベント(クリックイベントなど)が発生した場合、キューに入れられて後で実行されます。
setTimeout(function(){
/* Some long block of code... */
setTimeout(arguments.callee, 10);
}, 10);
setInterval(function(){
/* Some long block of code... */
}, 10);
setInterval()
SetTimeoutに似ていますが、キャンセルされるまで継続的に関数を呼び出します(毎回遅延あり)。
setTimeoutコードには、少なくとも10ミリ秒の遅延が常にあります。
前回のコールバックの実行(最終的にはそれ以上になることはありますが、決して減ることはありません)に対して、setIntervalは、最後のコールバックがいつ実行されたかに関係なく、10msごとにコールバックを実行しようとします。
タイマーがすぐに実行されないようにブロックされている場合、遅延されます
次の可能な実行ポイントまで(希望する遅延よりも長くなります)。間隔は、実行に十分な時間がかかる場合(指定された時間よりも長い場合)、遅延なくバックツーバックで実行される場合があります。
ディレイ)。
私の経験に関するメモです。setTimeout()を使用する場合、関数は常に遅延する(つまり、後で実行されることを意味します)か、「タイムアウト」されていないコードの後に実行されます。これは、delay = 0で、イベントループが原因で発生する関数でも発生する可能性があります。
以下の例をご覧ください。
setTimeout(function() {
console.log('Second');
}, 0)
console.log('First');