イベントループ - nextTickとマイクロタスク - NodeJSのESMモードとCJSモードでnextTickと.thenの実行順序が矛盾する

イベントループ

今日は、nodejs タイプ「module」がイベント ループに及ぼす影響を共有したいと思います。

まずはイベントループの基礎知識を復習しましょう。

一般的に同期 --> 非同期、マクロタスク --> マイクロタスク (マクロタスクは非同期タスクと非同期タスクに分けられます)同期、同期マクロ --> マイクロ --> 非同期マクロ 順番にループします)

詳細は、同期タスク (スクリプト) -----> マイクロタスク キューのクリア-----> マクロ タスクです。 ...ループ

注意してくださいマイクロタスクをクリアしてください。マイクロタスク中に再度マイクロタスクを追加すると、マイクロタスクは引き続き実行されます。

マクロ タスク中にマイクロ タスクが見つかった場合、現在のマクロ タスク キューにまだタスクが待機している場合でも、最初に処理される次のマクロ タスクに進む前に最初にマイクロ タスクを完了します。

マクロタスクとミクロタスクとは何ですか?

マクロタスク:

  1. スクリプト (外側の 同期 コードとして理解できます)
  2. setTimeout/setInterval
  3. UI rendering/UI事件
  4. 投稿メッセージ,メッセージチャンネル
  5. I/O 操作、setImmediate (Node.js) (setImmediate とは何ですか? a>setImmediate? a>setImmediate は setTimeout よりも優先されます) (タイマーを実際には 0 ミリ秒にすることはできないため、より遅くなります) setImmediate より)

マイクロタスク:

  1. 約束してください。それでは
  2. queueMicrotask(() => {}) マイクロタスクを作成する
  3. Object.observe (非推奨; Proxy オブジェクトに置き換えられました)
  4. ミュータイオンオブザーバー

NodeJS的nextTick队列

  1. process.nextTick (Node.js) (別のキューがあり、 はマイクロタスクよりも前に実行されます)

公式原文: Node.js イベント ループの各ラウンドで、process.nextTick() キューは常にマイクロタスク キューの前に処理されます。

何か問題が発生しました - NodeJS でこのコードを実行します

実際、今日の焦点は上記の内容ではなく、上記の知識を基に、次のコードを実行してみます。

    console.log('1');
    setTimeout(function () {//time1
        console.log('2');
        new Promise(function (resolve) {
            console.log('4');
            resolve();
        }).then(function () {//then1
            console.log('5')
        })
        process.nextTick(function () {//next1
            console.log('3');
        })
    })
    process.nextTick(function () {//next2
        console.log('6');
    })
    new Promise(function (resolve) {
        console.log('7');
        resolve();
    }).then(function () {//then2
        console.log('8')
    })
    setTimeout(function () {//time2
        console.log('9');
        process.nextTick(function () {//next3
            console.log('10');
        })
        new Promise(function (resolve) {
            console.log('11');
            resolve();
        }).then(function () {//then3
            console.log('12')
        })
    })

従来の分析によれば、nextTick はマイクロタスクの前に実行され、結果は 1 7 6 8< となるはずです。 a i=3> 2 4 3 5 9 11 10 12

package.json のタイプが設定されていない場合、または「commonjs」に設定されている場合、出力結果は実際に次のようになります。私たちのアイデアと一致しています。

画像.png

画像.png

ただし、type:"module" を設定すると、出力結果が変わり、 が得られます。 1 7 8 6 2 4 3 5 9 11 10 12

画像.png

画像.png

問題の分析と解決策

Promise.then と process.nextTick が同時に出現するコードをさらにいくつか試してみると、期待と矛盾していることがわかります。

単純に「モジュールモードのマイクロタスクの方が優先度が高い」と考えていいのでしょうか?

いいえ!上記のコードに戻ってもう一度観察すると、「Promise.then と process.nextTick が同時に表示される」ことが複数回あることがわかります。最終的には a>。言い換えれば、最も外側のマイクロタスクと nextTick だけが不正な順序の問題を抱えています。 最外層のみがシーケンスを変更します

全員で複数の資料を確認した結果、最終的な理由は次のとおりです。ESM モードでは、コードは実際には async/await で実行されます

つまり、処理された ESM コードは実際には次のようになります。

画像.png

したがって、ESM の実行中、コードは実際にはマイクロタスク フェーズにあり、nextTick の番になる前にマイクロタスク キューをクリアする必要があります。。 CommonJS は同期的に実行されるため、期待どおりの結果が得られます

これは、最も外側の nexttick のみに順序の問題がある理由の良い説明にもなります。実際、ルールは公式ドキュメントと同じです。Node.js イベント内ループ 各ラウンドでは、process.nextTick() キューは常にマイクロタスク キューの前に処理されます。最も外側のコードはマイクロタスク内にあり、もちろん nextTick ターンはありません。 setTimeout の NextTick は Promise.then の前に実行されます。

ここで、このコードに戻ってください。

// CommonJS模式 - 普通分析即可
// 在esm模式中, 由于esm会被包裹在await后执行,所以相当于第一轮代码执行是微任务,这时候的NextTick需要等待微任务清空完成后才能执行。所以先输出 8 再 6。
// 后续在time1中又遇到了nextTick和微任务同时出现的情况,这时候就是普通宏任务环境了,所以 nextTick优先级高于微任务, 先输出 3 再 5
const d = () => {
    console.log('1');
    setTimeout(function () {//time1
        console.log('2');
        new Promise(function (resolve) {
            console.log('4');
            resolve();
        }).then(function () {//then1
            console.log('5')
        })
        process.nextTick(function () {//next1
            console.log('3');
        })
    })
    process.nextTick(function () {//next2
        console.log('6');
    })
    new Promise(function (resolve) {
        console.log('7');
        resolve();
    }).then(function () {//then2
        console.log('8')
    })
    setTimeout(function () {//time2
        console.log('9');
        process.nextTick(function () {//next3
            console.log('10');
        })
        new Promise(function (resolve) {
            console.log('11');
            resolve();
        }).then(function () {//then3
            console.log('12')
        })
    })
}
d()

要約する

面接中にイベント ループの問題に遭遇した場合、nextTick に遭遇した場合は、CommonJS と ESM の違いを明確に説明してくださいと思います。役に立ちます 追加のポイントをもたらします

参考文献:

  1. 順序が一致しない理由 (コメントを参照):commonJs と ESM の NodeJS process.nextTick と queueMicrotask の実行順序は何ですか? _ビッグ データ ナレッジ ベース (saoniuhuo.com)
  2. ESM が非同期である理由: Node.js が ES6 モジュールを処理する方法 - Ruan Yifeng のネットワーク ログ (ruanyifeng.com)

おすすめ

転載: blog.csdn.net/m0_64130892/article/details/134536870