ホームページ >ウェブフロントエンド >jsチュートリアル >ブラウザとNodeJSのEventLoopの類似点、相違点、部分的な仕組み

ブラウザとNodeJSのEventLoopの類似点、相違点、部分的な仕組み

不言
不言オリジナル
2018-07-07 17:23:572070ブラウズ

この記事では、主にブラウザと NodeJS の EventLoop の類似点と相違点、およびいくつかのメカニズムを紹介します。必要な友人はそれを参照してください。ブラウザと NodeJS の EventLoop、およびいくつかのメカニズム

JavaScript はシングルスレッドのスクリプト言語ですが、開発者がスレッド ブロックの問題を解決するのに役立つ多くの非同期 API を備えています。例: onClick に登録されたコールバック関数、必須の ajax など...しかし、JavaScript 実行環境では、常にスレッドをブロックしたり、さまざまな非同期操作の完了を待ってから操作の実行を続行したりすることなく、どのようにしてシングル スレッドを実現できるのでしょうか? 答えは次のとおりです:

イベントループ


1.event loop 的规范是在HTML5中规定的。
2.event loop 是 javascript 运行环境(手动加粗) 的机制。
3.浏览器实现的event loop 与 NodeJS 实现的event loop 是有异同的。
HTML5で定義されているイベントループ仕様リンク https://www.w3.org/TR/html5/w...

ブラウザイベントループ

1.

イベントループの簡単な理解 イベントループとは何ですか? Ruan Yifeng 先生のブログには写真が付いていますが、非常に単純明快ですが、いくつかの点が欠けており、イベント ループの全体的な循環メカニズムを完全に実証することはできません。まずは写真を見てみましょう:


ブラウザとNodeJSのEventLoopの類似点、相違点、部分的な仕組み

写真は作者によるオリジナルではなく、Ruan Yifeng のブログからのものであり、侵害され削除されていることに注意してください。

画像から得られる情報は次のとおりです:

1. 実行中および実行待ちのさまざまなイベントを含むスタックが 1 つしかないため、JavaScript エンジンは JavaScript を単一のスレッドで実行します。

2. 一部の WebAPI は、実行中に生成されたコールバックをキュー、つまり「イベント キュー」に入れます。

3. イベント ループでは、「イベント キュー」で実行を待機しているイベントが継続的に JavaScript 実行スタックにプッシュされます。

これがイベントループの単純化の仕組みです。なぜ単純化と言うのでしょうか?ループ内で言及されていない操作やルールが多数あるためです。
栗はあげませんが、例えてみましょう。



ブラウザとNodeJSのEventLoopの類似点、相違点、部分的な仕組み

よくある問題について話しましょう(記事を編集するのが不便なので、1行だけです、改行すると私を殴るでしょう!)ブラウザとNodeJSのEventLoopの類似点、相違点、部分的な仕組み

setTimeout(e=>{ console.log(1) },0);

new Promise((res,rej)=>{ res() }).then(e=>{ console.log(2) });
それらは非同期でもありますAPI は JavaScript で提供されており、それらはすべて同じです (開発者が望んでいることですが、ブロックによる遅延が発生し、活用が妨げられます)。誰がこれら 2 行のコードを入力または終了しても、出力は変わりません。

2 1

になります。これには、イベントループ内の

マクロタスクマイクロタスクの実行順序とルールが関係するためです。

2. 全体的なプロセス

先ほどのフローチャートが十分に完璧ではないという問題に戻り、完全かつ包括的なイベント ループのフローチャートを見てみましょう。

ブラウザとNodeJSのEventLoopの類似点、相違点、部分的な仕組み

写真は作者のオリジナルではなく、JavaScript 忍者の秘密から来ています。侵害または削除されないことに注意してください。

これはイベント ループの完全なフローチャートです。最初から最後まで (上から下に) 並べてみましょう。

1.マクロタスクキュータスク。状況は 2 つあります

タスク キューが空で、下方向に実行します
  • タスク キューが空ではなく、最初の
  • one
  • (記事内では手動 + 太字) タスクを JavaScript 実行スタックにプッシュして実行します下向き

    2. マイクロタスクキュー内のタスクを読み取ります。状況は 2 つあります
タスクキューが空で、下方向に実行する
  • タスクキューが空ではない、最初に入力されたタスクを JavaScript 実行スタックにプッシュし、
  • この操作をもう一度繰り返す
  • (マニュアル + 記事追加)粗)マイクロタスクキューが空になるまで。率直に言うと:

    すべてのタスクをこのタスク キューから順番に JavaScript 実行スタックにプッシュし、下方向に実行します

    3. このサイクルの時間に基づいて
  • を決定します (手動 + 太字)記事)
が必要、
UI を更新できるかどうか [このサイクル タイムの問題については後で説明します] 必要ありません。最初のステップを繰り返します
  • 必要に応じて、下に進みます

  • 4. UI を更新します、UI レンダリング、JavaScript の実行をブロックします。そして引き続きステップ 1 を繰り返します。

    上記はイベント ループ プロセス全体です。このプロセスから、これら 2 つのキューを JavaScript にインスタンス化する API が
Macrotask queue --> setTimeout || setInterval || javascript代码

Microtask queue --> Promise.then()
であることがわかります。この時点で、完全なイベント ループ プロセスは次のようになります。完全に完成しました。

3.实例解析
什么鬼?这么复杂? 弄懂?不存在的
ブラウザとNodeJSのEventLoopの類似点、相違点、部分的な仕組み

现在回到刚才提到的 “老生常谈的问题” 从实例的角度来说明一下问题。我们假设这个 javascript 文件叫做 "main.js"
"main.js"中的代码(+ 为自定义标记)

+1 console.log(1);

+2 setTimeout(e=>{ console.log(2); },0)

+3 setTimeout(e=>{ console.log(3); },0)

+4 new Promise((resolve,reject)=>{ console.log(4); resolve();})
.then(e=>{ console.log(5); })

+5 setTimeout(e=>{ console.log(6);

  +6 new Promise((resolve,reject)=>{ console.log(7); resolve(); })
     .then(e=>{ console.log(8);})
})

那么这个执行顺序是怎样呢?从头带尾梳理一遍(词穷,全文只要是流程统一是“从头到尾梳理一遍”)

macrotask: javascript 代码,所有同步代码执行。输出:1 4。注册 +4 到 microtask。 注册+2 +3 +5 到 macrotask。
microtask: 执行 +4 输出:5
macrotask: 执行 +2。 输出 2
microtask:
macrotask: 执行 +3。 输出 3
microtask:
macrotask: 执行 +5。 输出 6 7。 注册 +6 到 microtask。
microtask: 输出 8

所以总体输出的顺序为:1 4 5 2 3 6 7 8

如果这个输出与你所想相同,那么基本就没有问题了。
那么如果不对或者有问题怎么办?
ブラウザとNodeJSのEventLoopの類似点、相違点、部分的な仕組み

PS: 前面提到 【本次循环耗时】这个问题,这里我也不是非常清楚,望大牛指点。浏览器一般渲染页面60/S,以达到每秒60帧(60 fps),所以大概16ms一次,既然有了时间我们不经就会问?前面的任务处理耽误了则么办?因为javascript线程与UI线程互斥,某些任务导致 javascript引擎 坑了队友,自然而然没法在16ms的节点上到达这一步,从secrets of javascript ninja中了解到,一般会摒弃这次渲染,等待下一次循环。( 如有问题请指正! )

浏览器中的 event loop 到此结束,下面说说 NodeJS 的 event loop



二 NodeJS的event loop

NodeJS 的 event loop 也是有 Macrotask queue 与 Microtask queue 的。只不过 NodeJS 的略有不同。那么主要说说不同在哪里。

NodeJS中 Macrotask queue 与 Microtask queue 实例化到API为:

Macrotask queue --> script(主程序代码),setImmediate, I/O,setTimeout, setInterval

Microtask queue --> process.nextTick, Promise

1.Macrotask queue 不同之处

上面说到了浏览器 event loop 的 Macrotask queue 在每次循环中只会读取一个任务,NodeJS 中 Macrotask queue 会一次性读取完毕( 同阶段的执行完毕,后面会说到Macrotask queue 分为 6个阶段 ),然后向下读取Microtask。

注意: 这一条与 NodeJS版本有很大关系,在看 深入浅出NodeJS 这一本书时( 看的版本很旧,不知是否有修订版,如有请告知。 ),提到的 setImmediate 每次循环只会执行一次,并且给出的示例在 v8.9.1 版本跑时已不符合书中所写。书中示例如下(+ 为自定义标记,原文中没有):
+1 process.nextTick(function () {
       console.log('nextTick执行1');
   });

+2 process.nextTick(function () {
       console.log('nextTick执行2');
   });

+3 setImmediate(function () {
       console.log('setImmediateჽ执行1');

    +4 process.nextTick(function () {
           console.log('强势插入');
       });
   });

+5 setImmediate(function () {
       console.log('setImmediateჽ执行2');
   });

+6 console.log('正常执行');

正常执行
nextTick执行1
nextTick执行2
setImmediate执行1
强势插入
setImmediateჽ执行2

在 v8.9.1 中截图如下
ブラウザとNodeJSのEventLoopの類似点、相違点、部分的な仕組み

从图片中可以看到,至少在 v8.9.1 版本中 Macrotask queue 会直接全部执行。按照惯例从头到尾的梳理一遍

macrotask: javascript 代码,所有同步代码执行。输出:正常执行。注册 +3 +5 到 Macrotask。执行process.nextTick(),最终输出:正常执行, nextTick执行1, nextTick执行2。
  **microtask: 无
macrotask: 执行 +3 +5。 输出:setImmediate执行1, setImmediateჽ执行2。 执行process.nextTick(),最终输出:setImmediate执行1, setImmediateჽ执行2,强势插入。
microtask:

所以最终输出为:正常执行, nextTick执行1, nextTick执行2,setImmediate执行1, setImmediateჽ执行2,强势插入。


2.process.nextTick(),setImmediates,以及event loop的6个阶段

NodeJS 中 Macrotask queue会分为 6 个阶段,每个阶段的作用如下(process.nextTick()在6个阶段结束的时候都会执行):

timers:执行setTimeout() 和 setInterval()中到期的callback。

I/O callbacks:上一轮循环中有少数的I/Ocallback会被延迟到这一轮的这一阶段执行

idle, prepare:仅内部使用

poll:最为重要的阶段,执行I/O callback,在适当的条件下会阻塞在这个阶段

check:执行setImmediate的callback

close callbacks:执行close事件的callback,例如socket.on("close",func)
注:此6个阶段非笔者原创来自 https://cnodejs.org/topic/5a9...,文章从底层C代码分析NodeJS event loop。这里做只做简单整合。侵删。

在了解了这六个阶段后,我们可以发现定时器系列在NodeJS event loop中 Macrotask queue 读取顺序为:

1. setTimeout(fun,0) setInterval(fun,0) 
2. setImmediate

空口无凭,在实例中了解。的代码奉上( 代码较长,分为三段,方便阅读,避免滚动。 ):

+1 process.nextTick(function(){
    console.log("1");
});
+2 process.nextTick(function(){
    console.log("2");
    +3 setImmediate(function(){
        console.log("3");
    });
    +4 process.nextTick(function(){
        console.log("4");
    });
});

+5 setImmediate(function(){
    console.log("5");
    +6 process.nextTick(function(){
        console.log("6");
    });
    +7 setImmediate(function(){
        console.log("7");
    });
});
+8 setTimeout(e=>{
    console.log(8);
    +9 new Promise((resolve,reject)=>{
        console.log(8+"promise");
        resolve();
    }).then(e=>{
        console.log(8+"promise+then");
    })
},0)

+10 setTimeout(e=>{ console.log(9); },0)

+11 setImmediate(function(){
    console.log("10");
    +12 process.nextTick(function(){
        console.log("11");
    });
    +13 process.nextTick(function(){
        console.log("12");
    });
    +14 setImmediate(function(){
        console.log("13");
    });
});
console.log("14");

+15 new Promise((resolve,reject)=>{
    console.log(15);
    resolve();
}).then(e=>{
    console.log(16);
})

这么复杂的异步嵌套在一起是不是很头疼呢?
我!不!看!了!

ブラウザとNodeJSのEventLoopの類似点、相違点、部分的な仕組み

最後のコーミング、最大かつ最も完全なコーミング。古くから最初から最後まで徹底的に調べてください

macrotask: JavaScriptコード、すべて同期コード実行。出力: 14。 process.nextTick() を実行し、最終出力: 14、15、1、2、4。 +3 +5 +8 +11 をマクロタスクに登録します。 Microtask に +15 を登録してください。
マイクロタスク: 実行+15出力16
マクロタスク:実行+8 +10出力8、8promise、9。 Microtask に +9 を登録してください。
microtask: +9を実行して8promise+then
macrotask: +5 +11 +3を実行して5、10、3を出力します。 +7 +14 を マクロタスク に登録します。 process.nextTick() を実行し、最終出力: 5 10 3 6 11 12。
マイクロタスク: なし
マクロタスク: 実行 +7 +14。 出力: 7, 13
microtask: None

したがって、合計出力は次のようになります: 14, 15, 1, 2, 4, 8, 8promise, 9, 8promise+then, 5, 10, 3 , 6, 11、12、7、13



三つ終わり

これで終わりです。ブラウザと NodeJS のイベント ループはすべて分析されており、その過程で、Ruan Yifeng のブログ、Zhihu、CSDN の記事の内容の一部が引用され、削除されました。

最近、いくつかの基本的な知識を理解することで多くのことを学びました。これらには、... に関するものや、あらゆる種類の奇妙な質問が含まれます。時間があれば書き留めます。

上記がこの記事の全内容です。その他の関連コンテンツについては、PHP 中国語 Web サイトをご覧ください。

関連する推奨事項:

JavaScriptを使用してブラウザの種類を決定する

Nodeを使用して静的ファイルサービスを提供する

JSブラウザのイベントループメカニズム

以上がブラウザとNodeJSのEventLoopの類似点、相違点、部分的な仕組みの詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

声明:
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。