Javascript 事件循环上下文中微任务和宏任务之间的区别
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/25915634/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Difference between microtask and macrotask within an event loop context
提问by NicBright
I've just finished reading the Promises/A+ specification and stumbled upon the terms microtask and macrotask: see http://promisesaplus.com/#notes
我刚刚阅读了 Promises/A+ 规范,偶然发现了术语 microtask 和 macrotask:参见http://promisesaplus.com/#notes
I've never heard of these terms before, and now I'm curious what the difference could be?
我以前从未听说过这些术语,现在我很好奇它们之间的区别是什么?
I've already tried to find some information on the web, but all I've found is this post from the w3.org Archives (which does not explain the difference to me): http://lists.w3.org/Archives/Public/public-nextweb/2013Jul/0018.html
我已经尝试在网上找到一些信息,但我发现的只是 w3.org 档案中的这篇文章(它没有向我解释区别):http: //lists.w3.org/Archives /Public/public-nextweb/2013Jul/0018.html
Additionally, I've found an npm module called "macrotask": https://www.npmjs.org/package/macrotaskAgain, it is not clarified what the difference exactly is.
此外,我发现了一个名为“macrotask”的 npm 模块:https: //www.npmjs.org/package/macrotask同样,它并没有说明究竟有什么区别。
All I know is, that it has something to do with the event loop, as described in https://html.spec.whatwg.org/multipage/webappapis.html#task-queueand https://html.spec.whatwg.org/multipage/webappapis.html#perform-a-microtask-checkpoint
我所知道的是,它与事件循环有关,如https://html.spec.whatwg.org/multipage/webappapis.html#task-queue和https://html.spec.whatwg 中所述.org/multipage/webappapis.html#perform-a-microtask-checkpoint
I know I should theoretically be able to extract the differences myself, given this WHATWG specification. But I'm sure that others could benefit as well from a short explanation given by an expert.
我知道理论上我应该能够根据这个 WHATWG 规范自己提取差异。但我相信其他人也可以从专家给出的简短解释中受益。
回答by NicBright
One go-around of the event loop will have exactly onetask being processed from the macrotask queue(this queue is simply called the task queuein the WHATWG specification). After this macrotask has finished, all available microtaskswill 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.
事件循环的一次循环将正好从宏任务队列中处理一个任务(该队列在WHATWG 规范中简称为任务队列)。在这个宏任务完成后,所有可用的微任务都将被处理,即在同一个复飞周期内。在处理这些微任务的同时,它们还可以排队更多的微任务,这些微任务都会一一运行,直到微任务队列耗尽。
What are the practical consequences of this?
这样做的实际后果是什么?
If a microtaskrecursively 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.
如果一个微任务递归地将其他微任务排队,则可能需要很长时间才能处理下一个宏任务。这意味着,您最终可能会遇到一个阻塞的 UI,或者您的应用程序中一些已完成的 I/O 空闲。
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 microtasksafter this limit is reached which allows the next macrotaskto be processed)
然而,至少有关的Node.js的process.nextTick函数(队列microtasks),存在通过process.maxTickDepth的手段对这种阻挡一个内置的保护。此值设置为默认值 1000,在达到此限制后减少对微任务的进一步处理,从而允许处理下一个宏任务)
So when to use what?
那么什么时候使用呢?
Basically, use microtaskswhen 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.
基本上,使用microtasks当你需要在同步异步方式做的东西(即当你会说在最近的将来执行这个(微)任务)。否则,坚持使用macrotasks。
Examples
例子
macrotasks:setTimeout, setInterval, setImmediate, requestAnimationFrame, I/O, UI rendering
microtasks:process.nextTick, Promises, queueMicrotask, MutationObserver
宏任务:setTimeout、setInterval、setImmediate、requestAnimationFrame、I/O、UI 渲染
微任务:process.nextTick、Promises、queueMicrotask、MutationObserver
回答by wengeezhang
Basic concepts in spec:
规范中的基本概念:
- An event loop has one or more task queues.(task queue is macrotask queue)
- Each event loop has a microtask queue.
- task queue = macrotask queue != microtask queue
- a task may be pushed into macrotask queue,or microtask queue
- when a task is pushed into a queue(micro/macro),we mean preparing work is finished,so the task can be executed now.
- 一个事件循环有一个或多个任务队列。(任务队列是宏任务队列)
- 每个事件循环都有一个微任务队列。
- 任务队列 = 宏任务队列!= 微任务队列
- 一个任务可能被推入宏任务队列,或微任务队列
- 当一个任务被推入队列(微/宏)时,我们的意思是准备工作已经完成,所以现在可以执行任务了。
And the event loop process model is as follows:
而事件循环流程模型如下:
when call stackis empty,do the steps-
当调用堆栈为空时,执行以下步骤-
- select the oldest task(task A) in task queues
- if task A is null(means task queues is empty),jump to step 6
- set "currently running task" to "task A"
- run "task A"(means run the callback function)
- set "currently running task" to null,remove "task A"
- perform microtask queue
- (a).select the oldest task(task x) in microtask queue
- (b).if task x is null(means microtask queues is empty),jump to step (g)
- (c).set "currently running task" to "task x"
- (d).run "task x"
- (e).set "currently running task" to null,remove "task x"
- (f).select next oldest task in microtask queue,jump to step(b)
- (g).finish microtask queue;
- jump to step 1.
- 选择任务队列中最早的任务(任务 A)
- 如果任务 A 为空(表示任务队列为空),则跳转到步骤 6
- 将“当前正在运行的任务”设置为“任务 A”
- 运行“任务A”(表示运行回调函数)
- 将“当前正在运行的任务”设置为空,删除“任务 A”
- 执行微任务队列
- (a).选择微任务队列中最早的任务(任务x)
- (b).如果任务 x 为空(意味着微任务队列为空),跳转到步骤(g)
- (c). 将“当前正在运行的任务”设置为“任务 x”
- (d). 运行“任务 x”
- (e).将“当前正在运行的任务”设置为空,删除“任务x”
- (f).选择微任务队列中下一个最早的任务,跳转到步骤(b)
- (g). 完成微任务队列;
- 跳到第 1 步。
a simplified process model is as follows:
一个简化的过程模型如下:
- run the oldest task in macrotask queue,then remove it.
- run all available tasks in microtask queue,then remove them.
- next round:run next task in macrotask queue(jump step 2)
- 运行宏任务队列中最旧的任务,然后将其删除。
- 运行微任务队列中的所有可用任务,然后删除它们。
- 下一轮:运行宏任务队列中的下一个任务(跳第2步)
something to remember:
要记住的事情:
- when a task (in macrotask queue) is running,new events may be registered.So new tasks may be created.Below are two new created tasks:
- promiseA.then()'s callback is a task
- promiseA is resolved/rejected: the task will be pushed into microtask queue in current round of event loop.
- promiseA is pending: the task will be pushed into microtask queue in the future round of event loop(may be next round)
- setTimeout(callback,n)'s callback is a task,and will be pushed into macrotask queue,even n is 0;
- promiseA.then()'s callback is a task
- task in microtask queue will be run in the current round,while task in macrotask queue has to wait for next round of event loop.
- we all know callback of "click","scroll","ajax","setTimeout"... are tasks,however we should also remember js codes as a whole in script tag is a task(a macrotask) too.
- 当任务(在宏任务队列中)运行时,可能会注册新事件。因此可能会创建新任务。以下是两个新创建的任务:
- promiseA.then() 的回调是一个任务
- promiseA 被解决/拒绝:任务将在当前轮事件循环中被推入微任务队列。
- promiseA 未决:该任务将在未来一轮的事件循环中被推入微任务队列(可能是下一轮)
- setTimeout(callback,n)的回调是一个任务,会被推入macrotask队列,即使n为0;
- promiseA.then() 的回调是一个任务
- 微任务队列中的任务将在本轮运行,而宏任务队列中的任务必须等待下一轮事件循环。
- 我们都知道"click","scroll","ajax","setTimeout"...的回调是任务,但是我们也应该记住脚本标签中的js代码整体也是一个任务(宏任务)。
回答by user1660210
I think we can't discuss event loop in separation from stack, so:
我认为我们不能脱离堆栈来讨论事件循环,所以:
JS has three "stacks":
JS 有三个“栈”:
- 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)
- 所有同步调用的标准堆栈(一个函数调用另一个函数等)
- 用于所有具有更高优先级的异步操作的微任务队列(或作业队列或微任务堆栈)(process.nextTick、Promises、Object.observe、MutationObserver)
- 用于所有具有较低优先级(setTimeout、setInterval、setImmediate、requestAnimationFrame、I/O、UI 渲染)的异步操作的宏任务队列(或事件队列、任务队列、宏任务队列)
|=======|
| macro |
| [...] |
| |
|=======|
| micro |
| [...] |
| |
|=======|
| stack |
| [...] |
| |
|=======|
And event loop works this way:
事件循环是这样工作的:
- execute everything from bottom to top from 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 microtask queue is empty or don't require any execution and ONLY then check macro stack
- check macro stack and execute everything there (if required) with help of stack
- 从堆栈底部到顶部执行所有内容,并且仅当堆栈为空时,检查上面队列中发生的事情
- 检查微堆栈并在堆栈的帮助下执行那里的所有内容(如果需要),一个接一个的微任务,直到微任务队列为空或不需要任何执行,然后才检查宏堆栈
- 检查宏堆栈并在堆栈的帮助下执行那里的所有内容(如果需要)
Mico stack won't be touch if stack isn't empty. Macro stack won't be touch if micro stack isn't empty OR do not require any execution.
如果堆栈不为空,则不会接触 Mico 堆栈。如果微堆栈不为空或不需要任何执行,则不会接触宏堆栈。
To sum up: microtask queue is almost the same as macrotask queue but those tasks (process.nextTick, Promises, Object.observe, MutationObserver)have higher priority than macrotasks.
总结一下:微任务队列与宏任务队列几乎相同,但这些任务(process.nextTick、Promises、Object.observe、MutationObserver)比宏任务具有更高的优先级。
Micro is like macro but with higher priority.
微观就像宏观,但具有更高的优先级。
Here you have "ultimate" code for understanding everything.
在这里,您拥有理解一切的“终极”代码。
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
*/

