11
2- # Microtasks 和事件循环
2+ # 微任务( Microtasks)
33
44Promise 的处理程序(handlers)` .then ` 、` .catch ` 和 ` .finally ` 都是异步的。
55
6- 即便一个 promise 立即被 resolve,` .then ` 、` .catch ` 和 ` .finally ` ** 下面 * * 的代码也会在这些处理程序之前被执行。
6+ 即便一个 promise 立即被 resolve,` .then ` 、` .catch ` 和 ` .finally ` * 下面 * 的代码也会在这些处理程序之前被执行。
77
88示例代码如下:
99
@@ -21,9 +21,9 @@ alert("code finished"); // 该警告框会首先弹出
2121
2222为什么 ` .then ` 会在之后被触发?这是怎么回事?
2323
24- ## Microtasks(微任务 )
24+ ## 微任务队列(Microtasks queue )
2525
26- 异步任务需要适当的管理。为此,JavaScript 标准指定了一个内部队列 ` PromiseJobs ` ,通常被称为 “microtask 队列 ”(v8 术语)。
26+ 异步任务需要适当的管理。为此,JavaScript 标准规定了一个内部队列 ` PromiseJobs ` ,通常被称为 “微任务队列 ”(v8 术语)。
2727
2828如[ 规范] ( https://tc39.github.io/ecma262/#sec-jobs-and-job-queues ) 中所述:
2929
@@ -32,7 +32,7 @@ alert("code finished"); // 该警告框会首先弹出
3232
3333或者,简单地说,当一个 promise 准备就绪时,它的 ` .then/catch/finally ` 处理程序就被放入队列中。但是不会立即被执行。当 JavaScript 引擎执行完当前的代码,它会从队列中获取任务并执行它。
3434
35- 这就是示例中的 “code finished” 会首先出现的原因。
35+ 这就是示例中的“code finished”会首先出现的原因。
3636
3737![ ] ( promiseQueue.png )
3838
@@ -52,78 +52,6 @@ Promise.resolve()
5252
5353现在代码就是按照预期执行的。
5454
55- ## 事件循环
56-
57- 浏览器内的 JavaScript 以及 Node.js 的执行流程都是基于** 事件循环** 的。
58-
59- “事件循环”是引擎休眠并等待事件的过程。只有当事件发生时才会处理它们,然后重新进入休眠状态。
60-
61- 事件可能来自外部,例如用户操作,或者也可能来自于内部任务的结束信号。
62-
63- 例如下面的事件:
64- - ` mousemove ` ,用户移动了他们的鼠标。
65- - ` setTimeout ` 处理程序将被调用。
66- - 一个外部的 ` <script src="..."> ` 被加载完成,准备执行。
67- - 网络操作,例如 ` fetch ` 被完成。
68- - 等等。
69-
70- 事情发生了 —— 引擎处理它们 —— 并等待更多的事情发生(在休眠时消耗接近零 CPU)。
71-
72- ![ ] ( eventLoop.png )
73-
74- 如你所见,这里也有一个队列。所谓的 “macrotask(宏任务)队列”(v8 术语)。
75-
76- 当一个事件发生时,如果引擎正忙,它的处理程序就会进入这个队列排队等待执行。
77-
78- 例如,当引擎忙于处理网络 ` fetch ` 请求时,用户可能会移动他们的鼠标,这会触发 ` mousemove ` ,或者相应的 ` setTimeout ` 等等,正如上图所示。
79-
80- 来自 macrotask 队列的事件基于“先进 —— 先处理”的原则被处理。当浏览器引擎完成 ` fetch ` 时,它会接着处理 ` mousemove ` 事件,然后是 ` setTimeout ` 处理程序,等等。
81-
82- 到目前为止,很简单,对吧?引擎正忙,所以其他任务需要排队。
83-
84- 现在重要的东西来了。
85-
86- ** Microtask 队列的优先级高于 macrotask 队列。**
87-
88- 换句话说,引擎会首先执行所有的 microtask,然后执行 macrotask。
89-
90- 例如,下面的代码:
91-
92- ``` js run
93- setTimeout (() => alert (" timeout" ));
94-
95- Promise .resolve ()
96- .then (() => alert (" promise" ));
97-
98- alert (" code" );
99- ```
100-
101- 顺序是什么?
102-
103- 1 . ` code ` 第一个出现,因为它是一个常规的同步调用。
104- 2 . ` promise ` 第二个出现,因为 ` .then ` 通过 microtask 队列被执行,并在当前代码之后运行。
105- 3 . ` timeout ` 最后出现,因为它来自于 macrotask 队列。
106-
107- 可能会发生这样的情况,在处理 macrotask 时,新的 promise 被创建。
108-
109- 或者,反过来说,一个 microtask 调度了一个 macrotask(例如 ` setTimeout ` )。
110-
111- 例如,这里的 ` .then ` 调度了一个 ` setTimeout ` :
112-
113- ``` js run
114- Promise .resolve ()
115- .then (() => {
116- setTimeout (() => alert (" timeout" ), 0 );
117- })
118- .then (() => {
119- alert (" promise" );
120- });
121- ```
122-
123- 当然,` promise ` 会首先出现,因为 ` setTimeout ` 宏任务在一个低优先级的 macrotask 队列中进行等待。
124-
125- 作为一个合乎逻辑的结果,只有当 promise 给引擎一个“空闲时间”时才处理 macrotask。因此,如果我们有一系列不等待任何事情的 promise 处理程序,它们会被一个接一个地执行,` setTimeout ` (或者用户操作处理程序)永远不能在它们之间运行。
126-
12755## 未处理的 rejection
12856
12957还记得 < info:promise-error-handling > 一章中“未处理的 rejection”事件吗?
@@ -132,60 +60,53 @@ Promise.resolve()
13260
13361** “未处理的 rejection”是指在 microtask 队列结束时未处理的 promise 错误。**
13462
135- 例如,考虑以下的代码 :
63+ 正常来说,如果我们预期可能会发生错误,我们会添加 ` .catch ` 到 promise 链上去处理它 :
13664
13765``` js run
13866let promise = Promise .reject (new Error (" Promise Failed!" ));
67+ * ! *
68+ promise .catch (err => alert (' caught' ));
69+ */ ! *
13970
140- window .addEventListener (' unhandledrejection' , event => {
141- alert (event .reason ); // Promise Failed!
142- });
71+ // 不会运行:错误已被处理
72+ window .addEventListener (' unhandledrejection' , event => alert (event .reason ));
14373```
14474
145- 我们创建一个 rejected 的 ` promise ` 并且没有处理错误。所以我们有了一个 “未处理的 rejection”(也会在控制台打印出来)。
146-
147- 如果我们添加了 ` .catch ` ,我们就不会见到这个“未处理的 rejection”,如下所示:
75+ ……但是如果我们忘记添加 ` .catch ` ,那么微任务队列清空后,JavaScript 引擎会触发以下事件:
14876
14977``` js run
15078let promise = Promise .reject (new Error (" Promise Failed!" ));
151- * ! *
152- promise .catch (err => alert (' caught' ));
153- */ ! *
15479
155- // 没有错误
80+ // Promise Failed!
15681window .addEventListener (' unhandledrejection' , event => alert (event .reason ));
15782```
15883
159- 如下所示,现在我们假设会在 ` setTimeout ` 之后抓住这个错误 :
84+ 如果我们迟点再处理这个错误会怎样?比如 :
16085
16186``` js run
16287let promise = Promise .reject (new Error (" Promise Failed!" ));
16388* ! *
16489setTimeout (() => promise .catch (err => alert (' caught' )));
16590*/ ! *
16691
167- // 错误: Promise Failed!
92+ // Error: Promise Failed!
16893window .addEventListener (' unhandledrejection' , event => alert (event .reason ));
16994```
17095
171- 现在再次出现“未处理的 rejection”。为什么?因为 ` unhandledrejection ` 在 microtask 队列完成时才会被生成。而引擎会检查 promise,如果其中的任何一个出现 “rejected” 状态,` unhandledrejection ` 事件就会被触发。
172-
173- 在这个例子中,` .catch ` 当然被 ` setTimeout ` 触发器添加了,只是会在 ` unhandledrejection ` 出现之后被执行。
96+ 现在,如果你运行以上的代码,我们先会看到 ` Promise Failed! ` 的消息,然后才是 ` caught ` 。
17497
175- ## 总结
98+ 如果我们并不了解微任务队列,我们可能想知道:“为什么 ` unhandledrejection ` 的处理程序会运行?我们已经去捕捉(catch)这个错误了!”
17699
177- - Promise 处理始终是异步的,因为所有 promise 操作都被放入内部的 “ promise jobs” 队列执行,也被称为 “microtask 队列”(v8 术语) 。
100+ 但是现在我们知道 ` unhandledrejection ` 在 microtask 队列完成时才会被生成:引擎会检查 promise,如果其中的任何一个出现“rejected”状态, ` unhandledrejection ` 事件就会被触发 。
178101
179- ** 因此, ` .then/ catch/finally ` 处理程序总是在当前代码完成后才被调用。 **
102+ 在这个例子中,被添加到 ` setTimeout ` 的 ` . catch` 也会执行,只是会在 ` unhandledrejection ` 事件出现之后才执行,所以并没有改变什么(没有发挥作用)。
180103
181- 如果我们需要确保一段代码在 ` .then/catch/finally ` 之后被执行,最好将它添加到 ` .then ` 的链式调用中。
182-
183- - 还有一个 “macrotask 队列”,用于保存各种事件,网络操作结果,` setTimeout ` —— 调度的方法,等等。这些也被称为 “macrotasks(宏任务)”(v8 术语)。
104+ ## 总结
184105
185- 引擎使用 macrotask 队列按出现顺序处理它们 。
106+ Promise 处理始终是异步的,因为所有 promise 操作都被放入内部的“promise jobs”队列执行,也被称为“微任务队列”(v8 术语) 。
186107
187- ** Macrotasks 在当前代码执行完成并且 microtask 队列为空时执行 。**
108+ ** 因此, ` .then/catch/finally ` 处理程序总是在当前代码完成后才被调用 。**
188109
189- 换句话说,它们的优先级较低 。
110+ 如果我们需要确保一段代码在 ` .then/catch/finally ` 之后被执行,最好将它添加到 ` .then ` 的链式调用中 。
190111
191- 所以顺序是:常规代码,然后是 promise 处理程序,然后是其他的一切,比如事件等等 。
112+ 在大部分 JavaScript 引擎中(包括浏览器和 Node.js),微任务的概念与“事件循环”和“宏任务”紧密联系。由于这些概念跟 promises 没有直接关系,它们被涵盖在本教程另外的章节 < info:event-loop > 中 。
0 commit comments