ホームページ  >  記事  >  ウェブフロントエンド  >  JavaScriptのイベントループの仕組みを詳しく解説

JavaScriptのイベントループの仕組みを詳しく解説

青灯夜游
青灯夜游転載
2018-10-08 15:30:432266ブラウズ

この記事では、JavaScript イベント ループ メカニズムに関する関連知識ポイントを共有します。興味のある方は、学習して参照してください。

ご存知のとおり、JavaScript はシングルスレッド言語です。Web-Worker は html5 で提案されましたが、シングルスレッドである JavaScript の核心は変わっていません。 HTML 仕様のこの部分を参照してください:

イベント、ユーザー インタラクション、スクリプト、レンダリング、ネットワーキングなどを調整するには、ユーザー エージェントはこのセクションで説明されているように 2 種類のイベント ループを使用する必要があります。イベント ループ: コンテキストの閲覧用とワーカー用のループです。

イベント、ユーザー インタラクション、スクリプト、UI レンダリング、およびネットワーク処理を調整するには、ユーザー エンジンはイベント ループを使用する必要があります。イベント ループは 2 つのタイプで構成されます。1 つはブラウジング コンテキストに基づくもの、もう 1 つはワーカーに基づくもので、どちらも独立して実行されます。次の記事では、例を使用して、コンテキストの閲覧に基づくイベント ループ メカニズムに焦点を当てます。

次の JavaScript コードを見てみましょう:

console.log('script start');

setTimeout(function() {
 console.log('setTimeout');
}, 0);

Promise.resolve().then(function() {
 console.log('promise1');
}).then(function() {
 console.log('promise2');
});

console.log('script end');

まず、このコードの出力シーケンスを推測し、それをブラウザーのコンソールに入力して、実際の出力シーケンスが一致するかどうかを確認します。推測された順序は一致していますか? そうであれば、JavaScript イベント ループのメカニズムをある程度理解していることを意味し、実際の出力順序が推測と一致しない場合は、この記事の次の部分を読んでください。あなたの質問に答えます。

タスク キュー

すべてのタスクは、同期タスクと非同期タスクに分類できます。同期タスクは、その名前が示すように、通常、メイン スレッドに直接実行されるタスクです。実行; 非同期タスクは、ajax ネットワーク リクエスト、setTimeout タイミング関数など、非同期で実行されるタスクであり、すべての非同期タスクはタスク キュー (イベント キュー) メカニズムを通じて調整されます。次の図を使用して、具体的な状況を大まかに説明します。

# 同期タスクと非同期タスクはそれぞれ異なる実行環境に入ります。同期タスクは、メイン スレッドに入ります。メイン実行スタック、および非同期タスクがメインスレッド、つまりメイン実行スタックに入ります。実行後にメイン スレッド内のタスクが空の場合、対応するタスクがイベント キューから読み取られ、実行のためにメイン スレッドにプッシュされます。上記のプロセスが継続的に繰り返されることをイベント ループと呼びます。

イベント ループでは、各ループ操作はティックと呼ばれます。仕様を読むと、各ティックのタスク処理モデルが比較的複雑であることがわかり、その主要なステップは次のように要約できます。

  • ##このティックで最初にキューに入った最も古いタスクを選択します (存在する場合)。それを (1 回) 実行します。

  • マイクロタスクが存在するかどうかを確認します。存在する場合は、マイクロタスク キューがクリアされるまで実行を続けます

  • Update render

メインスレッドは上記の手順を繰り返します


画像を使用してプロセスを説明できます:

#「マイクロタスクとは何ですか?」と聞きたい人もいると思います。仕様ではタスクを分割することが規定されています。マクロ タスク (マクロ タスク) とマイクロ タスク (マイクロ タスク) の 2 つのカテゴリに分かれており、各マクロ タスクが完了した後、すべてのマイクロ タスクをクリアする必要があります。ここでのマクロ タスクは、一部の記事ではタスクと呼ばれるものでもありません。以下の記事で説明されているタスクはすべてマクロ タスクとみなされます。

(マクロ)タスクの主な内容: スクリプト (コード全体)、setTimeout、setInterval、I/O、UI インタラクティブ イベント、setImmediate (Node.js 環境)

マイクロタスクの主な内容: Promise、 MutaionObserver、process.nextTick (Node.js 環境)

setTimeout/Promise などの API がタスクソースとなり、タスクキューに入るのはそれらで指定された特定の実行タスクです。異なるタスク ソースからのタスクは、異なるタスク キューに入ります。このうち、setTimeoutとsetIntervalは由来が同じです。

サンプルコードの分析

単語数は数千なので、例を使ってわかりやすく説明する方がよいでしょう。以下では、仕様に従い、上記の例を段階的に分析していきます。まず、サンプル コードを投稿します (上にスクロールする手間を省くため)。

console.log('script start');

setTimeout(function() {
 console.log('setTimeout');
}, 0);

Promise.resolve().then(function() {
 console.log('promise1');
}).then(function() {
 console.log('promise2');
});

console.log('script end');

スクリプト全体が最初のマクロ タスクとしてメイン スレッドに入り、console.log に遭遇し、script startを出力します

    setTimeout に遭遇し、そのコールバック関数が配布されます
  • がマクロ タスク イベント キューで Promise に遭遇すると、その then 関数がマイクロ タスク イベント キューに割り当てられ、then1 として記録されます。次に、then 関数に再度遭遇し、割り当てられます。それをマイクロタスクのイベントキューに追加し、タスクのイベントキューに then2
  • として記録されます。console.log に遭遇すると、スクリプトの終了
  • ## が出力されます。

    この時点では、次の表に示すように、イベント キューに 3 つのタスクが存在します。

マクロ タスクマイクロtask setTimeoutthen1-then2
  • 执行微任务,首先执行then1,输出 promise1, 然后执行 then2,输出 promise2,这样就清空了所有微任务

  • 执行 setTimeout 任务,输出 setTimeout 至此,输出的顺序是:script start, script end, promise1, promise2, setTimeout

so,你猜对了吗?

看看你掌握了没

再来一个题目,来做个练习:

console.log('script start');

setTimeout(function() {
 console.log('timeout1');
}, 10);

new Promise(resolve => {
 console.log('promise1');
 resolve();
 setTimeout(() => console.log('timeout2'), 10);
}).then(function() {
 console.log('then1')
})

console.log('script end');

这个题目就稍微有点复杂了,我们再分析下:

首先,事件循环从宏任务 (macrotask) 队列开始,最初始,宏任务队列中,只有一个 scrip t(整体代码)任务;当遇到任务源 (task source) 时,则会先分发任务到对应的任务队列中去。所以,就和上面例子类似,首先遇到了console.log,输出 script start; 接着往下走,遇到 setTimeout 任务源,将其分发到任务队列中去,记为 timeout1; 接着遇到 promise,new promise 中的代码立即执行,输出 promise1, 然后执行 resolve ,遇到 setTimeout ,将其分发到任务队列中去,记为 timemout2, 将其 then 分发到微任务队列中去,记为 then1; 接着遇到 console.log 代码,直接输出 script end 接着检查微任务队列,发现有个 then1 微任务,执行,输出then1 再检查微任务队列,发现已经清空,则开始检查宏任务队列,执行 timeout1,输出 timeout1; 接着执行 timeout2,输出 timeout2 至此,所有的都队列都已清空,执行完毕。其输出的顺序依次是:script start, promise1, script end, then1, timeout1, timeout2

用流程图看更清晰:

总结

有个小 tip:从规范来看,microtask 优先于 task 执行,所以如果有需要优先执行的逻辑,放入microtask 队列会比 task 更早的被执行。

最后的最后,记住,JavaScript 是一门单线程语言,异步操作都是放到事件循环队列里面,等待主执行栈来执行的,并没有专门的异步执行线程。

以上就是本章的全部内容,更多相关教程请访问JavaScript视频教程

以上がJavaScriptのイベントループの仕組みを詳しく解説の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事はjb51.netで複製されています。侵害がある場合は、admin@php.cn までご連絡ください。