diff --git a/6-async/07-microtask-queue/article.md b/6-async/07-microtask-queue/article.md new file mode 100644 index 0000000000..6faf08e085 --- /dev/null +++ b/6-async/07-microtask-queue/article.md @@ -0,0 +1,191 @@ + +# Microtasks 和事件循环 + +Promise 的处理程序(handlers)`.then`、`.catch` 和 `.finally` 都是异步的。 + +即便一个 promise 立即被 resolve,`.then`、`.catch` 和 `.finally` **下面**的代码也会在这些处理程序之前被执行。 + +示例代码如下: + +```js run +let promise = Promise.resolve(); + +promise.then(() => alert("promise done")); + +alert("code finished"); // 该警告框会首先弹出 +``` + +如果你运行它,你会首先看到 `code finished`,然后才是 `promise done`。 + +这很奇怪,因为这个 promise 绝对在开头就被执行了。 + +为什么 `.then` 会在之后被触发?这是怎么回事? + +## Microtasks(微任务) + +异步任务需要适当的管理。为此,JavaScript 标准指定了一个内部队列 `PromiseJobs`,通常被称为 “microtask 队列”(v8 术语)。 + +如[规范](https://tc39.github.io/ecma262/#sec-jobs-and-job-queues)中所述: + +- 队列是先进先出的:首先进入队列的任务会首先运行。 +- 只有在引擎中没有其它任务运行时,才会启动任务队列的执行。 + +或者,简单地说,当一个 promise 准备就绪时,它的 `.then/catch/finally` 处理程序就被放入队列中。但是不会立即被执行。当 JavaScript 引擎执行完当前的代码,它会从队列中获取任务并执行它。 + +这就是示例中的 “code finished” 会首先出现的原因。 + +![](promiseQueue.png) + +Promise 处理程序总是被放入这个内部队列中。 + +如果有一个 promise 链带有多个 `.then/catch/finally`,那么它们中每一个都是异步执行的。也就是说,它会首先排入一个队列,只有当前代码执行完毕而且先前的排好队的处理程序都完成时才会被执行。 + +**如果返回值的顺序对我们很重要该怎么办?我们怎么才能让 `code finished` 在 `promise done` 之后出现呢?** + +很简单,只需要像下面这样把返回 `code finished` 的 `.then` 处理程序放入队列中: + +```js run +Promise.resolve() + .then(() => alert("promise done!")) + .then(() => alert("code finished")); +``` + +现在代码就是按照预期执行的。 + +## 事件循环 + +浏览器内的 JavaScript 以及 Node.js 的执行流程都是基于**事件循环**的。 + +“事件循环”是引擎休眠并等待事件的过程。只有当事件发生时才会处理它们,然后重新进入休眠状态。 + +事件可能来自外部,例如用户操作,或者也可能来自于内部任务的结束信号。 + +例如下面的事件: +- `mousemove`,用户移动了他们的鼠标。 +- `setTimeout` 处理程序将被调用。 +- 一个外部的 `