Node.js のイベントループ機構について説明する記事

青灯夜游
リリース: 2021-11-08 18:30:45
転載
2328 人が閲覧しました

この記事は、Node.js のイベント ループ (タイム ループ) メカニズムを理解するのに役立ちます。

Node.js のイベントループ機構について説明する記事

今日は、nodeJ のイベント ループについて学びます。イベントループを理解するのは私にとって常に大きな困難でしたが、今回の学習を通じてその困難を突破したいと思いますし、このブログを通してイベントループについての理解と感想を深めていきたいと思っています。

libuv

イベントループを学ぶ前に、まずノードの libuv について理解してください。 libuv は、さまざまなオペレーティング システムでのさまざまな I/O モデルの実装を担当し、さまざまな実装をサードパーティ アプリケーションで使用できる API に抽象化します。

Node.js のイベントループ機構について説明する記事

質問

イベント ループを正式に学習する前に、質問について考えてみましょう

    setTimeout(() => {
      console.log("timer1");
      Promise.resolve().then(() => {
        console.log("promise1");
      });
    }, 0);

    setTimeout(() => {
      console.log("timer2");
      Promise.resolve().then(() => {
        console.log("promise2");
      });
    }, 0);
ログイン後にコピー

このコードは何ですかブラウザで実行した結果でしょうか?

Node.js のイベントループ機構について説明する記事

ノードで実行した結果はどうなりますか?

#ノード 8.6 前:

Node.js のイベントループ機構について説明する記事

##ノード 8.6 後:


Node.js のイベントループ機構について説明する記事なぜこのような結果になるのか、後で分析します!

nodeJs のイベントループ

まず、画像を見てみましょう:

Node.js のイベントループ機構について説明する記事#画像には 6 つのステージ、つまりタイマー、保留中のコールバック、アイドル/準備、ポーリング、チェック、クローズ コールバックが表示されます。

    タイマー フェーズ: 主に setTimeOut、setInterval コールバックを実行します。
  • 保留中のコールバック フェーズ: ネットワーク通信エラー コールバックなどの一部のシステム コール エラーを実行します
  • アイドル/準備フェーズ: システム内でのみ使用されます (このフェーズを制御したり干渉したりすることはできません)
  • ポーリング フェーズ: 新しい I/ を取得します。ファイルを読み取るための I/O コールバックの取得などの O イベント。
  • 適切な状況下では、nodejs はこのフェーズでブロックします

  • チェックフェーズ: setImmediate コールバックを実行します
  • たとえば、sokect を実行します。イベント コールバックの破棄とクローズ
  • 各ステージは、
FIFO

(先入れ先出し) ルールに従って、内部のタスク キュー タスクを実行します。 これら 6 つのステージのうち、タイマー、ポーリング、チェック ステージに焦点を当てます。私たちの日常の開発における非同期タスクのほとんどは、これら 3 つの段階で処理されます。

タイマー

まずタイマーの段階について話しましょう。

タイマーはイベント ループの最初のステージです。Nodejs は期限切れのタイマーがあるかどうかを確認し、期限切れになっている場合はコールバックをキューに入れます。ただし、nodejs は、事前に設定されたイベントが到着したときにすぐにタイマーがコールバックを実行することを保証できません。これは、nodejs によるタイマーの有効期限チェックが必ずしも信頼できるわけではないためです。マシン上で実行されている他のプログラムの影響を受けるか、現在のメインスレッドが発生しました。アイドル状態ではありません。
ここでの不確実性については、公式サイトに例が示されています: まずsetTimeOutを宣言してから、外部からファイルを読み込む このように、ファイルの読み込み操作がタイマー時間を超えた場合、読み込んだファイルは操作によりタイマーのコールバックが遅延します。これは、前述したようにメインスレッドがアイドル状態ではない状況です。

poll

ポーリング フェーズでは主に 2 つのことを実行します:

1. ポーリング フェーズのタスク キューの処理

2タイムアウトになったタイマーがある場合、そのコールバック関数が実行されます

Node.js のイベントループ機構について説明する記事上の図では、次のことも確認できます。

ポーリング タスク キューの実行後ポーリングフェーズ タスク終了後、プリセットsetImmediateが存在するかどうかを確認し、存在する場合はチェックフェーズに入り、存在しない場合はここでnodejsがブロックします。

ここで質問なのですが、投票段階でブロックされてしまうと、設定したタイマーは実行できなくなるのではないでしょうか?

実際には

イベント ループがポーリング フェーズでブロックされている場合、nodejs にはチェック メカニズムがあり、タイマー キューが空かどうかをチェックします。空でない場合は、タイマーに再入力されます。段階。

check

チェックフェーズでは主に setImmediate コールバック関数が実行されます。

小总结

event-loop的每个阶段都有一个队列,当event-loop达到某个阶段之后,将执行这个阶段的任务队列,直到队列清空或者达到系统规定的最大回调限制之后,才会进入下一个阶段。当所有阶段都执行完成一次之后,称event-loop完成一个tick。

案例

上面我们说完了event-loop的理论部分,但是光有理论我们也还是不能很清晰的理解event-loop。下面我们就根据几个demo来更加深入的理解下event-loop!

demo1

    const fs=require('fs')
    fs.readFile('test.txt',()=>{
            console.log('readFile')
            setTimeout(()=>{
                    console.log('settimeout');
            },0)
            setImmediate(()=>{
                    console.log('setImmediate')
            })
    })
ログイン後にコピー

执行结果:

Node.js のイベントループ機構について説明する記事

可见执行结果跟我们前面的分析时一致的!

demo2

    const fs = require("fs");
    const EventEmitter = require("events").EventEmitter;
    let pos = 0;
    const messenger = new EventEmitter();

    messenger.on("message", function (msg) {
      console.log(++pos + " message:" + msg); //
    });

    console.log(++pos + " first"); //

    process.nextTick(function () {
      console.log(++pos + " nextTick"); //
    });

    messenger.emit("message", "hello!");
    fs.stat(__filename, function () {
      console.log(++pos + " stat"); //
    });

    setTimeout(function () {
      console.log(++pos + " quick timer"); //
    }, 0);
    setTimeout(function () {
      console.log(++pos + " long timer"); //
    }, 30);
    setImmediate(function () {
      console.log(++pos + " immediate"); //
    });

    console.log(++pos + " last"); //
ログイン後にコピー

结果:

Node.js のイベントループ機構について説明する記事

了解下浏览器和node的event-loop差异在什么地方

在node 8.6 之前:

浏览器中的微任务队列会在每个宏任务执行完成之后执行,而node中的微任务会在事件循环的各个阶段之间执行,即每个阶段执行完成之后会去执行微任务队列。

在8.6之后:

浏览器和node中微任务的执行是一致的!

所以,在文章开头,我们提出的思考的问题就有了结果。

关于 process.nextTick()和setImmediate

process.nextTick()

语法:process.nextTick(callback,agrs)

执行时机:

这个函数其实是独立于 Event Loop 之外的,它有一个自己的队列,当每个阶段完成后,如果存在 nextTick 队列,就会清空队列中的所有回调函数,并且优先于其他 microtask 执行。递归的调用process.nextTick()会导致I/O starving,官方推荐使用setImmediate()

关于starving现象的说明:

    const fs = require("fs");
    fs.readFile("test.txt", (err, msg) => {
      console.log("readFile");
    });

    let index = 0;

    function handler() {
      if (index >= 30) return;
      index++;
      console.log("nextTick" + index);
      process.nextTick(handler);
    }

    handler();
ログイン後にコピー

运行结果:

Node.js のイベントループ機構について説明する記事

可以看到,等到nextTick函数呗执行30次之后,读取文件的回调才被执行!这样的现象被称为 I/O 饥饿

当我们把 process.nextTick 换为 setImmediate

    const fs = require("fs");
    fs.readFile("test.txt", (err, msg) => {
      console.log("readFile");
    });

    let index = 0;

    function handler() {
      if (index >= 30) return;
      index++;
      console.log("nextTick" + index);
      setImmediate(handler);
    }

    handler();
ログイン後にコピー

结果:

Node.js のイベントループ機構について説明する記事

造成这两种差异的原因是,嵌套调用的setImmediate的回调被排到了下一次event-loop中去!

event-loop核心思维导图

1Node.js のイベントループ機構について説明する記事

结束语

通过今天的学习,让我对event-loop的理解更深刻了。那么,下次见。好好学习,天天向上!

1Node.js のイベントループ機構について説明する記事

更多编程相关知识,请访问:编程视频!!

以上がNode.js のイベントループ機構について説明する記事の詳細内容です。詳細については、PHP 中国語 Web サイトの他の関連記事を参照してください。

関連ラベル:
ソース:juejin.cn
このウェブサイトの声明
この記事の内容はネチズンが自主的に寄稿したものであり、著作権は原著者に帰属します。このサイトは、それに相当する法的責任を負いません。盗作または侵害の疑いのあるコンテンツを見つけた場合は、admin@php.cn までご連絡ください。
最新の問題
人気のチュートリアル
詳細>
最新のダウンロード
詳細>
ウェブエフェクト
公式サイト
サイト素材
フロントエンドテンプレート