Macrotask, Microtask


Macrotask / Microtask


ブラウザ環境では、実際のヒステリシス時間は0ではありません.
ブラウザは、HTML 5標準に規定されたオーバーラップタイマ実行間隔の制限を遵守する.この基準では、「5番目のオーバーラップタイマの後、待機時間は少なくとも4ミリ秒以上に制限する必要があります.」このような制限が明示されている.
これらの制限については、例を参照してください.例のsetTimeoutは、関数runを遅延なく再呼び出すようにスケジューリングされる.アレイtimesには、実際の遅延間隔に関する情報が記録されており、アレイ時間にどのような値が格納されているかを見てみましょう.
let start = Date.now();
let times = [];

setTimeout(function run() {
  times.push(Date.now() - start); // 이전 호출이 끝난 시점과 현재 호출이 시작된 시점의 시차를 기록

  if (start + 100 < Date.now()) alert(times); // 지연 간격이 100ms를 넘어가면, array를 얼럿창에 띄워줌
  else setTimeout(run); // 지연 간격이 100ms를 넘어가지 않으면 재스케줄링함
});

// 출력창 예시:
// 1,1,1,1,9,15,20,24,30,35,40,45,50,55,59,64,70,75,80,85,90,95,100
前のタイマーはspecに書いてあるように、直接運転を遅らせることはありません.しかし、5番目のオーバーラップタイマの後、遅延間隔は4ミリ秒を超え、9, 15, 20, 24...と同じ値が格納されていることがわかる.
この制限は、setTimeoutだけでなく、setIntervalにも適用される.setInterval(f)度の前回の関数fは、遅延なしで動作したが、その後、遅延間隔を4ミリ秒以上に増やした.
これは以前から存在していた制約ですが、古いスクリプトの一部はこの制約に依存しているため、リストを変更することはできません.
一方,サーバ側にはこのような制限はない.Node.jsのprocess.nextTickおよびsetImmediateを用いて、非同期動作を遅延なく実行することができる.上記の制限はブラウザのみです.
ご存知のように、DOM変更を画面に表示するには、時間の長さにかかわらず、ブラウザが現在の作業タスクを完了する必要があります.
https://meetup.toast.com/posts/89
https://dev.to/lydiahallie/javascript-visualized-event-loop-3dif
https://stackoverflow.com/questions/25915634/difference-between-microtask-and-macrotask-within-an-event-loop-context


イベントループの詳細


One go-around of the event loop will have exactly one task being processed from the macrotask queue (this queue is simply called the task queue in the WHATWG specification ). After this macrotask has finished, all available microtasks will be processed, namely within the same go-around cycle. While these microtasks are processed, they can queue even more microtasks, which will all be run one by one, until the microtask queue is exhausted.

What are the practical consequences of this?


If a microtask recursively queues other microtasks, it might take a long time until the next macrotask is processed. This means, you could end up with a blocked UI, or some finished I/O idling in your application.
However, at least concerning Node.js's process.nextTick function (which queues microtasks), there is an inbuilt protection against such blocking by means of process.maxTickDepth. This value is set to a default of 1000, cutting down further processing of microtasks after this limit is reached which allows the next macrotask to be processed)

So when to use what?


Basically, use microtasks when you need to do stuff asynchronously in a synchronous way (i.e. when you would say perform this (micro-)task in the most immediate future). Otherwise, stick to macrotasks.

Examples


macrotasks: setTimeout , setInterval , setImmediate , requestAnimationFrame , I/O , UI rendering, event
microtasks: process.nextTick , Promises , queueMicrotask , MutationObserver
JS has three "stacks":
  • standard stack for all synchronous calls (one function calls another, etc)
  • microtask queue (or job queue or microtask stack) for all async operations with higher priority (process.nextTick, Promises, Object.observe, MutationObserver)
  • macrotask queue (or event queue, task queue, macrotask queue) for all async operations with lower priority (setTimeout, setInterval, setImmediate, requestAnimationFrame, I/O, UI rendering)
  • And event loop works this way:
  • execute everything from bottom to top from the stack, and ONLY when the stack is empty, check what is going on in queues above
  • check micro stack and execute everything there (if required) with help of stack, one micro-task after another until the microtask queue is empty or don't require any execution and ONLY then check the macro stack
  • check macro stack and execute everything there (if required) with help of the stack
  • Micro stack won't be touched if the stack isn't empty. The macro stack won't be touched if the micro stack isn't empty OR does not require any execution.

  • TaskがCalstackに入る

  • Taskの戻り値を返します.macrotaskに関連するタスクは、Wep APIからmacrotaskキューに渡されます.

  • Event loopは、Calstackが空いているときにMacroTaskのTaskをCalstackに配置して実行します.

  • その後、MicroTask QueueがアイドルになるまですべてのTaskを実行します.(1ループで動作可能なMicroTasksの数は固定されている.)

  • 次に、Macrotaskキューの次のタスクを実行します.