イベントループコンテキスト内のマイクロタスクとマクロタスクの違い


140

Promises / A +仕様を読み終えたところで、microtaskとmacrotaskという用語に出会いました。http://promisesaplus.com/#notesを参照してください

私はこれまでこれらの用語を聞いたことがありませんでした、そして今、私はその違いが何であるかについて知りたいですか?

私はすでにウェブ上でいくつかの情報を見つけようとしましたが、私が見つけたすべてはw3.org Archivesからのこの投稿です(これは私には違いを説明していません):http : //lists.w3.org/Archives /Public/public-nextweb/2013Jul/0018.html

さらに、「macrotask」と呼ばれるnpmモジュールを見つけました。https//www.npmjs.org/package/macrotask 繰り返しますが、正確な違いは明確ではありません。

で説明したように、すべてのI knowが、それはイベントループとは何かを持っていること、であるhttps://html.spec.whatwg.org/multipage/webappapis.html#task-queue//html.spec.whatwg:HTTPS .org / multipage / webappapis.html#perform-a-microtask-checkpoint

このWHATWG仕様を考えると、理論的には自分で違いを抽出できるはずです。しかし、専門家による短い説明から他の人も同様に恩恵を受けることができると私は確信しています。


つまり、複数のネストされたイベントキューです。自分で実装することもできます:while (task = todo.shift()) task();
Bergi

1
もう少し詳細を知りたい人のために:JavaScript忍者の秘密、第2版、第13章存続イベント
Ethan

回答:


220

イベントループの1つの回避策では、マクロタスクキューから処理されるタスクが1つだけあります(このキューは、WHATWG仕様では単にタスクキューと呼ばれます)。このマクロタスクが終了すると、使用可能なすべてのマイクロタスクが処理されます。つまり、同じゴーアラウンドサイクル内で処理されます。これらのマイクロタスクが処理されている間、さらに多くのマイクロタスクをキューに入れることができ、マイクロタスクキューがすべて使用されるまで、1つずつ実行されます。

これの実際の結果は何ですか?

場合はマイクロタスクが再帰的に他のマイクロタスクをキューに次のマクロタスクが処理されるまで、それは長い時間がかかる場合があります。つまり、アプリケーションでUIがブロックされたり、I / Oアイドリングが完了したりする可能性があります。

ただし、少なくともNode.jsのprocess.nextTick関数(マイクロタスクをキューに入れる)に関しては、process.maxTickDepthによるそのようなブロッキングに対する組み込みの保護があります。この値はデフォルトの1000に設定されており、この制限に達した後のマイクロタスクのさらなる処理を削減し、次のマクロタスクを処理できるようにします)

それで、いつ何を使うのですか?

基本的には、使用マイクロタスクをあなたは(あなたが言うすなわち、同期の方法で非同期的にものを行う必要があり、最も近い将来、この(マイクロ)タスクを実行します)。それ以外の場合は、マクロタスクに固執します。

マクロタスク: setTimeoutsetIntervalsetImmediaterequestAnimationFrameI / O、UIレンダリング
マイクロタスク: process.nextTickPromisesqueueMicrotaskMutationObserver


4
イベントループにはマイクロタスクのチェックポイントがありますが、これはほとんどの開発者がマイクロタスクに遭遇する場所ではありません。JSスタックが空になると、マイクロタスクが処理されます。これは、タスク内、またはイベントループのレンダリングステップ内でも何度も発生する可能性があります。
JaffaTheCake 2018

2
process.maxTickDepth非常に長い時間前に削除されました:github.com/nodejs/node/blob/...
RidgeA

また、queueMicrotask()メソッドを使用して新しいマイクロタスクを追加することもできます
ZoomAll

@ZoomAllに感謝、これまでqueueMicrotask()を知りませんでした。私はそれを回答に追加し、すべてのものへのリンクを追加しました...
NicBright

requestAnimationFrame(rAF)はマイクロタスクを生成するだけではありません。一般的に、rAFコールは別のキューを
ZoomAll

67

仕様の基本概念:

  • イベントループには1つ以上のタスクキューがあります(タスクキューはマクロタスクキューです)
  • 各イベントループにはマイクロタスクキューがあります。
  • タスクキュー=マクロタスクキュー!=マイクロタスクキュー
  • タスクはマクロタスクキューまたはマイクロタスクキューにプッシュされる可能性があります
  • タスクがキュー(マイクロ/マクロ)にプッシュされると、準備作業が完了したため、タスクをすぐに実行できます。

また、イベントループプロセスモデルは次のとおりです。

とき、コールスタックが空である、steps-を行います

  1. タスクキュー内の最も古いタスク(タスクA)を選択する
  2. タスクAがnullの場合(タスクキューが空であることを意味します)、ステップ6にジャンプします。
  3. 「現在実行中のタスク」を「タスクA」に設定します
  4. 「タスクA」を実行します(コールバック関数を実行することを意味します)
  5. 「現在実行中のタスク」をnullに設定し、「タスクA」を削除
  6. マイクロタスクキューを実行する
    • (a)。マイクロタスクキュー内の最も古いタスク(タスクx)を選択する
    • (b)。タスクxがnullの場合(マイクロタスクキューが空であることを意味する)、ステップ(g)にジャンプする
    • (c).set "currently running task" to "task x"
    • (d).run "タスクx"
    • (e).set "currently running task" to null、remove "task x"
    • (f)マイクロタスクキューで次に古いタスクを選択し、ステップ(b)にジャンプします
    • (g).finishマイクロタスクキュー;
  7. ステップ1にジャンプします。

簡略化されたプロセスモデルは次のとおりです。

  1. マクロタスクキューで最も古いタスクを実行してから削除します。
  2. マイクロタスクキューで使用可能なすべてのタスクを実行してから、それらを削除します。
  3. 次のラウンド:マクロタスクキューの次のタスクを実行(ジャンプステップ2)

覚えておくべきこと:

  1. (マクロタスクキュー内の)タスクが実行されている場合、新しいイベントが登録される可能性があります。そのため、新しいタスクが作成される場合があります。以下の2つの新しいタスクが作成されます。
    • promiseA.then()のコールバックはタスクです
      • promiseAは解決/拒否されます:タスクは、イベントループの現在のラウンドでマイクロタスクキューにプッシュされます。
      • promiseAは保留中です:タスクは、イベントループの次のラウンドでマイクロタスクキューにプッシュされます(次のラウンドになる可能性があります)
    • setTimeout(callback、n)のコールバックはタスクであり、nが0であってもマクロタスクキューにプッシュされます。
  2. マイクロタスクキュー内のタスクは現在のラウンドで実行されますが、マクロタスクキュー内のタスクは、イベントループの次のラウンドを待機する必要があります。
  3. "click"、 "scroll"、 "ajax"、 "setTimeout"のコールバックはすべてタスクであることがわかっています。ただし、スクリプトタグ内のjsコード全体がタスク(マクロタスク)であることも覚えておく必要があります。

2
これは素晴らしい説明です!共有してくれてありがとう!もう1つ言及する必要があるのは、NodeJsetImmediate()あり、マクロ/タスクでありprocess.nextTick()、マイクロ/ジョブです。
LeOn-ハンリ17年

6
ブラウザのpaintタスクはどうですか?彼らはどのカテゴリーに入るでしょうか?
Legends

マイクロタスク(のようなrequestAnimationFrame)に適していると思います
Divyanshu Maithani '

次に、v8イベントループが実行される順序を示します->コールスタック|| マイクロタスク|| タスクキュー|| rAF || レンダリングツリー|| レイアウト|| ペイント|| <画面にピクセルを描画するOSネイティブコール> <----- 1)DOM(新しい変更)、CSSOM(新しい変更)、Render Tree、レイアウト、およびペイントは、イベントループタイマーに従ってrequestAnimationFrameコールバック後に発生します。このため、rAFの前にDOM操作をできる限り終了することが重要です。残りはrAFで実行できます。PS:rAFを呼び出すとマクロタスクの実行がトリガーされます。
Anvesh Checka

私が間違っているかどうかはわかりませんが、この答えにはちょっと同意できません。マイクロタスクがマクロタスクの前に実行されます。codepen.io/walox/pen/yLYjNRq
walox

9

スタックから分離したイベントループについては説明できないので、次のようにします。

JSには3つの「スタック」があります。

  • すべての同期呼び出し用の標準スタック(ある関数が別の関数を呼び出すなど)
  • 優先度の高いすべての非同期操作用のマイクロタスクキュー(またはジョブキューまたはマイクロタスクスタック)(process.nextTick、Promises、Object.observe、MutationObserver)
  • 優先度の低いすべての非同期操作用のマクロタスクキュー(またはイベントキュー、タスクキュー、マクロタスクキュー)(setTimeout、setInterval、setImmediate、requestAnimationFrame、I / O、UIレンダリング)
|=======|
| macro |
| [...] |
|       |
|=======|
| micro |
| [...] |
|       |
|=======|
| stack |
| [...] |
|       |
|=======|

そしてイベントループはこのように機能します:

  • スタックから下から上にすべてを実行し、スタックが空の場合のみ、上のキューで何が起こっているかを確認します
  • マイクロスタックをチェックし、スタックの助けを借りて(必要に応じて)すべてを実行します。マイクロタスクキューが空になるか、実行が不要になるまで、マイクロタスクを次々と実行して、マクロスタックのみをチェックします
  • マクロスタックをチェックし、スタックの助けを借りてそこで(必要な場合)すべてを実行する

スタックが空でない場合、Micoスタックは接触しません。マイクロスタックが空でないか、実行を必要としない場合、マクロスタックは影響を受けません。

要約すると、マイクロタスクキューはマクロタスクキューとほとんど同じですが、それらのタスク(process.nextTick、Promises、Object.observe、MutationObserver)はマクロタスクよりも優先されます。

マイクロはマクロに似ていますが、優先度が高くなっています。

ここには、すべてを理解するための「究極の」コードがあります。

console.log('stack [1]');
setTimeout(() => console.log("macro [2]"), 0);
setTimeout(() => console.log("macro [3]"), 1);

const p = Promise.resolve();
for(let i = 0; i < 3; i++) p.then(() => {
    setTimeout(() => {
        console.log('stack [4]')
        setTimeout(() => console.log("macro [5]"), 0);
        p.then(() => console.log('micro [6]'));
    }, 0);
    console.log("stack [7]");
});

console.log("macro [8]");

/* Result:
stack [1]
macro [8]

stack [7], stack [7], stack [7]

macro [2]
macro [3]

stack [4]
micro [6]
stack [4]
micro [6]
stack [4]
micro [6]

macro [5], macro [5], macro [5]
--------------------
but in node in versions < 11 (older versions) you will get something different


stack [1]
macro [8]

stack [7], stack [7], stack [7]

macro [2]
macro [3]

stack [4], stack [4], stack [4]
micro [6], micro [6], micro [6]

macro [5], macro [5], macro [5]

more info: https://blog.insiderattack.net/new-changes-to-timers-and-microtasks-from-node-v11-0-0-and-above-68d112743eb3
*/

1
キューをスタックと呼ぶのは完全に混乱します。
ベルギ
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.