イベントループ内ブラウザおよびノード

序文

JS固有の非ブロックシングルスレッドのスクリプト言語。言語は、単一のスレッドとして、メインスレッドは、1つのタスクのみJSコード実行を実行します。
ノンブロッキング実装では、我々が話をしたいのイベントループに依存しています。イベントループ仕様はちょうど理解することが、本当に苦いと難しいですが、私はかむのは難しいことはお勧めしません。

プロセスとスレッド

jsがシングルスレッドの言語で言って。だからのスレッドが何であるか、フロントエンドの学生のほとんどのために、それはあまり明確ではないかもしれません。推奨この記事のグエン首長、鮮やかな
すべての最初は、それはすべてのコンピューティングタスクを想定し、コンピュータのCPUのコアです。それは常に動作して、工場のようなものです。

プロセス

工場の床のようなプロセスは、それが単一のCPUがタスクを処理することができ表します。
いずれかの時点で、CPUは常にプロセスを実行し、他のプロセスが実行されていません。
すなわち、リソース割り当ての最小単位、独立スタック領域とデータ記憶領域であります

スレッド

ワークショップでのスレッドのような労働者の比率。ワークショップのスペースが共有されているプロセスのメモリ空間を象徴する、労働者によって共有され、各スレッドは、これらの共有メモリを使用することができます。
つまり、プログラムの実行、複数のスレッドを含むことができ、処理の最小単位です。

スイッチングは、より効率的かつ少ないオーバーヘッドであるように、プロセスに関しては、スレッドは、オペレーショナル・データ・スペースを必要としません。

Jsのシングルスレッドの原点

明らかに、並列処理で複数のプロセスがCPU使用率を向上させます。
初期のjsのスクリプトが表示される。しかし、それはユーザへの表示を完了するために、DOMと対話しなければなりません。
もし複数のプロセス、DOMの同時操作、制御できないの、その後の結果。
例えば:同じボタンは、異なるプロセスがどのように表示最後に、異なる色を与え。

それはJSので、あなたが複数のスレッド+ロック、そしてあまりにも複雑を、使用している場合はスクリプト言語としては、シングルスレッド。

しかし、開発のJSで、単一のスレッドに、より多くの限られたを運ぶ能力はJS効率およびその他の制限が適用される場合があります。
これにより、非DOM操作を実行するには、Webワーカーを増やします。

しかし、非メインスレッドのスレッドがいくつかの制限があり、例えば、DOMなどを操作することはできません、それは彼らが心配ではないでしょうDOM操作の一貫性を確保するために、あります。

当社の能力または非ブロックの基礎の主な焦点は、それは、イベントループです。

イベントループブラウザ

彼はイベントループはイベントキューを開始すると発表しました。
メインスレッドの実行では、ヒープ(ヒープ)とスタック(スタック)を生成します。

ヒープメモリは、我々は、スタックメモリ空間を実行するための基本的なデータ型や関数を実行することです宣言されたデータのオブジェクトタイプです。

メインスレッドは、タスクキューのイベント、プロセスを絶えず循環しているので、イベントループで、この操作機構から読み込みます。


同期コードを直接実行されます。
また、別の方法は、非同期イベントキューを実行するときに追加しましたが、違いがある非同期イベントが、違いは、優先順位の実装であることになります。

イベントカテゴリ

それは非同期タスク間で同じではないので、それらの実行優先度が異なるため。マイクロタスク(マイクロタスク)とマクロタスク(マクロタスク):異なる非同期タスクは、2つのカテゴリに分けられます。

  • 次のイベントは、マクロタスクました:

setTimeout、のsetInterval、setImmediate、I / O、UIのレンダリング

  • 次のイベントは、マイクロタスクました

約束、Object.observe(廃止)、MutationObserver(HTML5の新機能)、process.nextTick

前に実行され、実行スタックコード(同期タスク)、常に「タスクキュー」を読んで(非同期タスク)
現在の実行スタックが終了したイベントキューにあるすべてのマイクロタスクを処理するために、すぐには、マクロタスクキューに行きますイベントを削除します。同一のイベントループで、マイクロマクロタスクは常にタスクの前に実行されます。

次のようにさまざまな種類のタスクの実行順序の場合:

  1. 同期コードの実行
  2. イベントループ開始
  3. マイクロタスクはキュー(実行)を空にし始めました
  4. タスクが空かどうか、4がスキップチェックし、何の6をスキップしません
  5. キューからタスクを抽出するタスクは、実行します
  6. マイクロタスクは空、ノースキップ3場合は2をスキップするかどうかを確認します
  7. イベントループの終わり

おそらく、チャートを流し、次のとおりです。

直接として栗を参照してください。

setTimeout(function () {
    console.log(1);
});

new Promise(function(resolve,reject){
    console.log(2)
    resolve(3)
}).then(function(val){
    console.log(val);
})
// 2 3 1 
  1. イベントの種類を区別:マクロタスクのsetTimeout、マイクロタスクを.then
  2. 2つの出力同期コードが実行されます
  3. タスクキュー空マイクロ出力3
  4. マクロタスク実行出力1

もう少し複雑に次のとおりです。

setTimeout(()=>{
    console.log('A');
},0);
var obj={
    func:function () {
        setTimeout(function () {
            console.log('B')
        },0);
        return new Promise(function (resolve) {
            console.log('C');
            resolve();
        })
    }
};
obj.func().then(function () {
    console.log('D')
});
console.log('E');
// c,e,d,b,a

あなたは例を挙げてそれを自分で試すことができます。

イベントループ機構ノードで

ノードにおいて、イベントループは、ほぼ同じブラウザの状態を表示します。違いは、ノードは、独自のモデルを持っているということです。
イベントループ内のノードを達成することはlibuvエンジンに依存することです。
私たちは、そのノードが、JS通訳として対応するノードを呼び出すためのV8エンジンjsのコード解析APIをクロームV8エンジンを選択、知っている
とlibuvエンジンドライバによってこれらの最後のAPI、対応するタスクを実行し、異なる上の異なるイベントメインスレッドのキュー待機。
だから、実際にはイベントループノードはlibuvエンジンに存在しています。

ノードのイベントは、次の主要な段階に分かれています。

  • タイマー:実装のsetTimeout()としたsetInterval()のコールバックセットのこのフェーズ。
  • I / Oコールバック:コールバック、タイマーコールバック、およびsetImmediate()コールバックを閉じるに加えて、ほぼすべてのコールバックを行います。
  • アイドル、準備:内部使用のみ。
  • 世論調査:新しいI / Oイベントを取得し、ノードは、新しいI / Oのを待って、適切な条件の下でここまで戻ってきます
  • チェック:プールフェーズの後、実行setImmediateは()コールバックを設定します。
  • クローズコールバック:このようsocket.onの実装(「近い」、...)コールバック

投票相

特別な注意その価値は投票段階

ステージは、以下の機能があります。

  1. 実行タイマーは、ミッションの時間制限の段階に達します。
  2. ポーリングタスクキューの実装フェーズ。

あなたは投票段階、および参加するノータスクタイマー段階に入る場合は、以下が起こります

  • 如果 poll 队列不为空的话,会执行 poll 队列直到清空或者系统回调数达到上限
  • 如果 poll 队列为空 ​ 如果设定了 setImmediate 回调,会直接跳到 check 阶段。 如果没有设定 setImmediate 回调,会阻塞住进程,并等待新的 poll 任务加入并立即执行。

process.nextTick()

nextTick 比较特殊,它有自己的队列,并且,独立于event loop。 它的执行也非常特殊,无论 event loop 处于何种阶段,都会在阶段结束的时候清空 nextTick 队列。

直接看例子吧:
process.nextTick

process.nextTick(function A() {
  console.log(1);
  process.nextTick(function B(){console.log(2);});
});

setTimeout(function timeout() {
  console.log('TIMEOUT FIRED');
}, 0)
// 1
// 2
// TIMEOUT FIRED

大概顺序如下:

  1. 因为nextTick的特殊性,当前阶段执行完毕,就执行。所以直接,输出1 2
  2. 执行到timer 输出 TIMEOUT FIRED

setImmediate

setImmediate(function A() {
  console.log(1);
  setImmediate(function B(){console.log(2);});
});

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

这个结果不固定,同一台机器测试结果也有两种:

// TIMEOUT FIRED =>1 =>2
或者
//  1=>TIMEOUT FIRED=>2
  1. 事件队列进入timer,性能好的 小于1ms,则不执行回调继续往下。若此时大于1ms, 则输出 TIMEOUT FIRED 就不输出步骤3了。
  2. poll阶段任务为空,存在setImmediate 直接进入setImmediate 输出1
  3. 然后再次到达timer 输出 TIMEOUT FIRED
  4. 再次进入check 阶段 输出 2

原因在于setTimeout 0 node 中至少为1ms,也就是取决于机器执行至timer时是否到了可执行的时机。

做个对比就比较清楚了:

setImmediate(function A() {
  console.log(1);
  setImmediate(function B(){console.log(2);});
});

setImmediate(function B(){console.log(4);});
setTimeout(function timeout() {
  console.log('TIMEOUT FIRED');
}, 20);
// 1=>2=>TIMEOUT FIRED

此时间隔时间较长,timer阶段最后才会执行,所以会先执行两次check,出处1,2
下面再看个例子
poll阶段任务队列

var fs = require('fs')

fs.readFile('./yarn.lock', () => {
    setImmediate(() => {
        console.log('1')
        setImmediate(() => {
            console.log('2')
        })
    })
    setTimeout(() => {
        console.log('TIMEOUT FIRED')
    }, 0)
    
})
// 结果确定:
// 输出始终为1=>TIMEOUT FIRED=>2
  1. 读取文件,回调进入poll阶段
  2. 当前无任务队列,直接check 输出1 将setImmediate2加入事件队列
  3. 接着timer阶段,输出TIMEOUT FIRED
  4. 再次check阶段,输出2

小结

浏览器的事件循环
浏览器比较清晰一些,就是固定的流程,当前宏任务结束,就是执行所有微任务(不一定是全部,可能基于系统能力,会有所剩下),然后再下一个宏任务,微任务这样交替进行。
node中的事件循环
主要是把握不同阶段和特殊情况的处理,特别是poll阶段和 process.nextTick任务。

结束语

参考文章:

https://zhuanlan.zhihu.com/p/47152694
https://html.spec.whatwg.org/multipage/webappapis.html#event-loop
http://www.ruanyifeng.com/blog/2014/10/event-loop.html
https://hackernoon.com/understanding-js-the-event-loop-959beae3ac40
https://juejin.im/post/5bac87b6f265da0a906f78d8
感谢上述参考文章,关于事件循环这里就总结完毕了,作为自己的一个学习心得。希望能帮助到有需求的同学,一起进步。

おすすめ

転載: www.cnblogs.com/pqjwyn/p/11238020.html