javascriptのイベントサイクルを理解する(Event Loop)
5482 ワード
背景
jsの非同期の実現方式を研究する時、JavaScrotaskとmicrotaskの概念を発見しました.一つの資料を見てから、その中の実行メカニズムを理解して、整理して、もっと多くの人を助けたいです.
まず、jsの任務執行メカニズムを理解してください.
まず、javascriptはシングルスレッドですので、非同期でしか性能問題を解決できません.jsはコードを実行する時2つの比較的に重要なものが存在します.スタックとタスクのキューを実行します.この2つのものはすべてタスクを格納するためのものです.タスクのキューには非同期のタスクが保存されています.これらの非同期のタスクは必ずスタックが空になってから実行します.タスクの行列については、「macrotask queue」という2つの種類に分けられています.厳密には「task」というだけで、「macrotask」という概念は言及されていません.ここでは、分かりやすいように、「macrotask=task!=microtask」とも解釈できます.node環境とブラウザ環境を同時に考えると、この二つのタスクはそれぞれ以下のappi:microtasksに対応します.process.nextTick promise Object.observe MuttionObserver macrotasks:setTimeout set Interval set Immedite I/O UIレンダリング scriptタグの全体コード javascriptは実行する時、先にmacrotasks列から実行を始めて、第一のmacrotaskを取り出して実行スタックに入れて実行して、実行する過程の中で、もしmacrotaskに出会うならば、このmacrotaskをmacrotask列に入れて、引き続き実行スタックの後続コードを実行します.もしmicrotaskに遭遇したら、microtaskをmicrotaskの列に入れて、引き続き下に向かって実行スタックの後続コードを実行します.実行スタックのコードが全部実行されたら、microtasksキューからすべてのmicrotaskを取り出して実行スタックに入れて実行します.実行後、再度macrotasksキューから次のmacrotaskを取り出して実行スタックに入れます.そして上記の流れを繰り返します.このプロセスはイベントループとも呼ばれる.javascriptはこのような仕組みで非同期を実現しています.メインストリームは、I/Oなどの非同期動作を一時的に記憶し、直接下に実行し、ある非同期イベントがトリガされた時に、メインスレッドに対応するコールバック関数を実行するように通知します.このような仕組みによって、Javascriptは、スレッド内の非同期動作の消耗を回避し、後続のタスクに対する影響を回避します.
図解イベントの循環フロー
図によると、イベントサイクルの実行手順は以下の通りである.1、macrotask queueから一番早いタスクを取り出し、実行スタックで最初に取り出したタスクを実行する.もしタスクの中にmicrotaskがあれば、microtask queueに押し込む.もしタスクの中にmacrotask queueがあれば、それをmacrotask queueに押し込む.macrotask queueから実行したmacrotask 5を削除し、microtask queueのすべてのタスクを取り出して、実行スタックに入れます.もしミッションの中にmicrotaskがあれば、それをmicrotask queueに押し込んで、もしミッションの中にmacrotaskがあれば、それをmacrotask queueに押し込みます.このステップでも実行されます.現在のmicrotask queueが空になるまで、このステップは終了します.6、第一歩の操作を実行する
インスタンス検証
私たちは下記のコードを実行して、上の考え方で実行します.結果が予想通りになるかどうか確認してください.
第2ラウンド:1、macrotask queueから一番早いタスクを取り出します.ここで対応するのは第1ラウンドの第3ステップのコールバック関数です.consolie.log('set Interval')は、set Interval 2、set Intervalのコールバック関数を出力して、macrotask.queueの中に入力します.次のイベントのサイクルを開始します.現在のコンソロジーでは、印刷内容:startpromise 1 setInterval現在のmacrotask queue:[set Timeout 1、set Interval]
第3ラウンド:1、macrotask queueから一番早いタスクを取り出します.現在はsetTimeout 1のフィードバックです.取り出したタスクを実行スタックに入れて実行します.2、consosolie.logsに会ったら、set Timeout 13を出力して、Promiseに会って、そしてresolive方法を呼び出して、再調整を行います.【promise 3,promise 4,()=>{set Timeout 2}4、microtask queueのタスクを実行スタック5に入れ、microtask queueのPromiseのフィードバックタスクを実行します.出力promise 3出力promise 4は、set Timeout 2をmacrotask queue 6に押し込み、実行スタックは空です.microtask queue 3は空です.次のイベントのプログラムの内容を印刷します.romise 4現在macrotask queue:[set Interval,set Timeout 2]
第四ラウンド:1、macrotask queueから一番早いタスクを取り出します.ここで対応するのはset Intervalで、set Interval 2、set Intervalを出力するコールバック関数はmacrotask queueにmacrotask queueとして入力します.3、実行スタックは空で、microtask queueは空で、次のイベントサイクルを開始します.現在のconsoneに印刷された内容:startpromise 1 promise 2 set IntervalsetTimeout 3 promise 4 set Intervalは現在、macrotask queue:[set Timeout 2、set Interval]
第5ラウンド:1、macrotask queueから一番早いタスクを取り出します.現在はsetTimeout 2のフィードバックです.取り出したタスクを実行スタックに入れて実行します.2、consolie.logに会ったらset Timeout 23、Promiseに会って、そしてresolive方法を呼び出して、再調整を行います.=>{clear Interval}4、microtask queue中のタスクを実行スタック5に入れ、microtask queue中のPromiseのフィードバックタスクを実行する:promise 5を出力してpromiter 6 clear Intervalを出力する.タイムアウトする.t 1 promise 3 promise 4 set IntervalsetTimeout 2 promise 5 promise 6
写真で見られます.結果は私たちの予想通りです.promise 2の後に方法の戻り値として、undefinedを多く印刷しました.これはよく理解できるはずです.
この中には小さな問題があります.それは違う環境の下にあります.promise 4の後のsetIntervalの表現には違いがあるかもしれません.ここではsetTimeoutとsetIntervalの最小間隔と関係があります.0 msと書いていますが、実際にはこの最小値は制限があります.現在の段階では組織や異なるjsエンジンの実現メカニズムに違いがありますが、この問題は今回の討論の範囲内ではありません.rvalの間隔は10に設定されていますが、全体の流れは私たちの予想にぴったりです.
何の役に立つかコードにPromiseを使って、set Timeoutを使うと、考えがより明確になり、より効果的に使えます. 一部のソースコードを読む時、一部のsetTimeoutに関する騒乱の操作がより深く理解できる. javascriptのタスクの流れを理解し、非同期の流れを理解し、ミスを少なくする. 締め括りをつけるjsイベントサイクルは常に1つのmacrotaskから実行される 一つのイベントサイクル中に、一つのmacrotaskしか実行しないが、複数のmicrotask スタック内のタスクから生じるmicrotaskは、現在のイベントサイクル内で実行される スタックのタスクから生じるmacrotaskは、次のイベントサイクルで実行される
jsの非同期の実現方式を研究する時、JavaScrotaskとmicrotaskの概念を発見しました.一つの資料を見てから、その中の実行メカニズムを理解して、整理して、もっと多くの人を助けたいです.
まず、jsの任務執行メカニズムを理解してください.
まず、javascriptはシングルスレッドですので、非同期でしか性能問題を解決できません.jsはコードを実行する時2つの比較的に重要なものが存在します.スタックとタスクのキューを実行します.この2つのものはすべてタスクを格納するためのものです.タスクのキューには非同期のタスクが保存されています.これらの非同期のタスクは必ずスタックが空になってから実行します.タスクの行列については、「macrotask queue」という2つの種類に分けられています.厳密には「task」というだけで、「macrotask」という概念は言及されていません.ここでは、分かりやすいように、「macrotask=task!=microtask」とも解釈できます.node環境とブラウザ環境を同時に考えると、この二つのタスクはそれぞれ以下のappi:microtasksに対応します.
図解イベントの循環フロー
図によると、イベントサイクルの実行手順は以下の通りである.1、macrotask queueから一番早いタスクを取り出し、実行スタックで最初に取り出したタスクを実行する.もしタスクの中にmicrotaskがあれば、microtask queueに押し込む.もしタスクの中にmacrotask queueがあれば、それをmacrotask queueに押し込む.macrotask queueから実行したmacrotask 5を削除し、microtask queueのすべてのタスクを取り出して、実行スタックに入れます.もしミッションの中にmicrotaskがあれば、それをmicrotask queueに押し込んで、もしミッションの中にmacrotaskがあれば、それをmacrotask queueに押し込みます.このステップでも実行されます.現在のmicrotask queueが空になるまで、このステップは終了します.6、第一歩の操作を実行する
インスタンス検証
私たちは下記のコードを実行して、上の考え方で実行します.結果が予想通りになるかどうか確認してください.
console.log('start')
const interval = setInterval(() => {
console.log('setInterval')
}, 0)
setTimeout(() => {
console.log('setTimeout 1')
Promise.resolve()
.then(() => {
console.log('promise 3')
})
.then(() => {
console.log('promise 4')
})
.then(() => {
setTimeout(() => {
console.log('setTimeout 2')
Promise.resolve()
.then(() => {
console.log('promise 5')
})
.then(() => {
console.log('promise 6')
})
.then(() => {
clearInterval(interval)
})
}, 0)
})
}, 0)
Promise.resolve()
.then(() => {
console.log('promise 1')
})
.then(() => {
console.log('promise 2')
})
上の考えに基づいて、私達は処理して、実行結果を予測して、実際の効果がこのようなですかを見てみます.実行プロセス:第1ラウンド:1、まずこの全体のjsコードは1つのmacrotaskとして先に実行されます.2、consolie.logに出会い、start 3を出力し、set Intervalに遭遇し、フィードバック関数はmacrotaskとしてmacrotask queueに入力します.この時、macrotask queue:[set Interval,set Timeout 1]5、Promiseに出会い、そしてresove方法を呼び出しました.フィードバックをトリガしました.microtask queueにmicrotaskとして押し込みました.promise 1をそれぞれ印刷します.promise 28、実行スタックは空です.microtask queueは空です.次のイベントサイクルを開始します.現在のsolieの印刷内容:startpromise 1 promise 2は現在、macrotask queue:[set Interval、setTimeout 1]第2ラウンド:1、macrotask queueから一番早いタスクを取り出します.ここで対応するのは第1ラウンドの第3ステップのコールバック関数です.consolie.log('set Interval')は、set Interval 2、set Intervalのコールバック関数を出力して、macrotask.queueの中に入力します.次のイベントのサイクルを開始します.現在のコンソロジーでは、印刷内容:startpromise 1 setInterval現在のmacrotask queue:[set Timeout 1、set Interval]
第3ラウンド:1、macrotask queueから一番早いタスクを取り出します.現在はsetTimeout 1のフィードバックです.取り出したタスクを実行スタックに入れて実行します.2、consosolie.logsに会ったら、set Timeout 13を出力して、Promiseに会って、そしてresolive方法を呼び出して、再調整を行います.【promise 3,promise 4,()=>{set Timeout 2}4、microtask queueのタスクを実行スタック5に入れ、microtask queueのPromiseのフィードバックタスクを実行します.出力promise 3出力promise 4は、set Timeout 2をmacrotask queue 6に押し込み、実行スタックは空です.microtask queue 3は空です.次のイベントのプログラムの内容を印刷します.romise 4現在macrotask queue:[set Interval,set Timeout 2]
第四ラウンド:1、macrotask queueから一番早いタスクを取り出します.ここで対応するのはset Intervalで、set Interval 2、set Intervalを出力するコールバック関数はmacrotask queueにmacrotask queueとして入力します.3、実行スタックは空で、microtask queueは空で、次のイベントサイクルを開始します.現在のconsoneに印刷された内容:startpromise 1 promise 2 set IntervalsetTimeout 3 promise 4 set Intervalは現在、macrotask queue:[set Timeout 2、set Interval]
第5ラウンド:1、macrotask queueから一番早いタスクを取り出します.現在はsetTimeout 2のフィードバックです.取り出したタスクを実行スタックに入れて実行します.2、consolie.logに会ったらset Timeout 23、Promiseに会って、そしてresolive方法を呼び出して、再調整を行います.=>{clear Interval}4、microtask queue中のタスクを実行スタック5に入れ、microtask queue中のPromiseのフィードバックタスクを実行する:promise 5を出力してpromiter 6 clear Intervalを出力する.タイムアウトする.t 1 promise 3 promise 4 set IntervalsetTimeout 2 promise 5 promise 6
写真で見られます.結果は私たちの予想通りです.promise 2の後に方法の戻り値として、undefinedを多く印刷しました.これはよく理解できるはずです.
この中には小さな問題があります.それは違う環境の下にあります.promise 4の後のsetIntervalの表現には違いがあるかもしれません.ここではsetTimeoutとsetIntervalの最小間隔と関係があります.0 msと書いていますが、実際にはこの最小値は制限があります.現在の段階では組織や異なるjsエンジンの実現メカニズムに違いがありますが、この問題は今回の討論の範囲内ではありません.rvalの間隔は10に設定されていますが、全体の流れは私たちの予想にぴったりです.
何の役に立つか