From d8ae8316fee769b4a4d1c3931aba527ad080d70f Mon Sep 17 00:00:00 2001 From: LeviDing Date: Sat, 4 Apr 2020 17:09:01 +0800 Subject: [PATCH 01/11] Update article.md --- 2-ui/99-ui-misc/03-event-loop/article.md | 28 ++++++++++++------------ 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/2-ui/99-ui-misc/03-event-loop/article.md b/2-ui/99-ui-misc/03-event-loop/article.md index 31e021fd2f..0626ba56b4 100644 --- a/2-ui/99-ui-misc/03-event-loop/article.md +++ b/2-ui/99-ui-misc/03-event-loop/article.md @@ -1,28 +1,28 @@ # 事件循环:微任务和宏任务 -JavaScript 在浏览器里的执行流程跟在 Node.js 中一样,是基于**事件循环**的。 +浏览器中 JavaScript 的执行流程和 Node.js 中的流程都是基于 **事件循环** 的。 -理解事件循环如何运行对于代码优化是重要的,有时对于正确的架构也很重要。 +理解事件循环的工作方式对于代码优化很重要,有时对于正确的架构也很重要。 -在本章中我们首先覆盖关于这个事情是如何运作的理论细节,然后看看这个知识的实际应用。 +在本章中,我们首先介绍有关事物工作方式的理论细节,然后介绍该只是的实际应用。 ## 事件循环 -**事件循环**的概念非常简单。它是一个在 JavaScript 引擎等待任务、执行任务和休眠等待更多任务这几个状态之间的无穷无尽的循环。 +**事件循环** 的概念非常简单。它是一个在 JavaScript 引擎等待任务,执行任务和进入休眠状态等待更多任务这几个状态之间转换的无限循环。 -执行引擎通用的算法: +引擎的一般算法: 1. 当有任务时: - - 从最先进入的任务开始执行 -2. 休眠到有新的任务进入,然后到第 1 步 + - 从最先进入的任务开始执行。 +2. 休眠直到出现任务,然后转到第 1 步。 -当我们浏览一个网页时就是上述这种形式。JavaScript 引擎大多数时候什么也不做,只在一个脚本、处理函数或者事件被激活时运行。 +当我们浏览一个网页时就是上述这种形式。JavaScript 引擎大多数时候不执行任何操作,仅在脚本/处理程序/事件激活时运行。 -任务举例: +任务示例: -- 当外部脚本 ` - ``` -……但是我们可能会想要在执行任务期间展示一些东西,例如进度条。 +……但是我们也可能想在任务执行期间展示一些东西,例如进度条。 -如果我们用 `setTimeout` 拆分繁重任务为小片段,值的改变就会在它们之间被绘制。 +如果我们使用 `setTimeout` 将繁重的任务拆分成几部分,那么变化就会被在它们之间绘制出来。 -这样看起来好多了: +这看起来更好看: ```html run
@@ -197,7 +197,7 @@ count(); function count() { - // 执行一些繁重的工作 (*) + // 做繁重的任务的一部分 (*) do { i++; progress.innerHTML = i; @@ -211,41 +211,42 @@ count(); count(); -``` +``` + +现在 `div` 显示了 `i` 的值的增长,这就是进度条的一种。 -现在 `div` 展示了 `i` 的值的增长,跟进度条很类似了。 ## 用例 3:在事件之后做一些事情 -在事件处理中我们可能要延期一些行为的执行,直到事件冒泡完成并被所有层级接手和处理之后。我们可以把这部分代码放在 0 延迟的 `setTimeout`。 +在事件处理程序中,我们可能会决定推迟某些行为,直到事件冒泡并在所有级别上得到处理后。我们可以通过将该代码包装到零延迟的 `setTimeout` 中来做到这一点。 -在[生成自定义事件](https://zh.javascript.info/dispatch-events)章节中,我们看到这样一个例子:自定义事件 `menu-open` 在 `setTimeout` 中被派发,所以它发生在“click”事件被完全处理后。 +在 一章中,我们看到过这样一个例子:自定义事件 `menu-open` 被在 `setTimeout` 中分派(dispatched),所以它在 `click` 事件被处理完成之后发生。 ```js menu.onclick = function() { // ... - // 创建一个附带被点击菜单项数据的自定义事件 + // 创建一个具有被点击的菜单项的数据的自定义事件 let customEvent = new CustomEvent("menu-open", { bubbles: true }); - // 异步派发自定义事件 + // 异步分派(dispatch)自定义事件 setTimeout(() => menu.dispatchEvent(customEvent)); }; ``` ## 宏任务和微任务 -伴随本章描述的**宏任务**还存在着**微任务**,在章节[微任务](https://zh.javascript.info/microtask-queue)有提及到。 +除了本章中所讲的 **宏任务(macrotask)** 外,还有在 一章中提到的 **微任务(microtask)**。 -微任务仅仅由我们的代码产生。它们通常由 promises 生成:对于 `.then/catch/finally` 的处理程序变成了一个微任务。微任务通常"隐藏在" `await` 下,因为它也是另一种处理 promise 的形式。 +微任务仅来自于我们的代码。它们通常是由 promise 创建的:对 `.then/catch/finally` 处理程序的执行会成为微任务。微任务也被用于 `await` 的“幕后”,因为它是 promise 处理的另一种形式。 -有一个特殊的函数 `queueMicrotask(func)`,可以将 `func` 加入到微任务队列来执行。 +还有一个特殊的函数 `queueMicrotask(func)`,它对 `func` 进行排队,以在微任务队列中执行。 -**在每个宏任务之后,引擎立即执行所有微任务队列中的任务,比任何其他的宏任务或者渲染或者其他事情都要优先。** +**每个宏任务之后,引擎会立即执行微任务队列中的所有任务,然后再执行其他的宏任务,或渲染,或进行其他任何操作。** -来看看例子: +例如,看看下面这个示例: ```js run setTimeout(() => alert("timeout")); @@ -256,23 +257,23 @@ Promise.resolve() alert("code"); ``` -这里的执行顺序是什么呢? +这里的执行顺序是怎样的? -1. `code` 首先出现,因为它是常规的同步调用。 -2. `promise` 第二个出现,因为 `then` 从微任务队列中来,在当前代码之后执行。 -3. `timeout` 最后出现,因为它是一个宏任务。 +1. `code` 首先显示,因为它是常规的同步调用。 +2. `promise` 第二个出现,因为 `then` 会通过微任务队列,并在当前代码之后运行。 +3. `timeout` 最后显示,因为它是一个宏任务。 -更详细的事件循环图如下: +更详细的事件循环图示如下(顺序是从上到下,即:首先是脚本,然后是微任务,渲染等): ![](eventLoop-full.svg) -**所有的微任务在任何其他的事件处理或者渲染或者任何其他的宏任务发生之前完成调用。** +微任务会在执行任何其他事件处理,或渲染,或执行任何其他宏任务之前完成。 -这非常重要,因为它保证了微任务中的程序运行环境基本一致(没有鼠标位置改变,没有新的网络返回数据,等等)。 +这很重要,因为它确保了微任务之间的应用程序环境基本相同(没有鼠标坐标更改,没有新的网络数据等)。 -如果我们想要异步执行(在当前代码之后)一个函数,但是要在修改被渲染或者新的事件被处理之前,我们可以用 `queueMicrotask` 来定时执行。 +如果我们想要异步执行(在当前代码之后)一个函数,但是要在更改被渲染或新事件被处理之前运行,那么我们可以使用 `queueMicrotask` 来对其进行安排(schedule)。 -还是跟前面的类似的“计数型进度展示条”的例子,不同的是用 `queueMicrotask` 来代替 `setTimeout`。你可以看到它在最后才渲染。就像写的是同步代码: +这是一个与前面那个例子类似的,带有“计数进度条”的示例,但是它使用了 `queueMicrotask` 而不是 `setTimeout`。你可以看到它在最后才渲染。就像写的是同步代码一样: ```html run
@@ -282,14 +283,16 @@ alert("code"); function count() { - // 执行一些繁重的工作 (*) + // 做繁重的任务的一部分 (*) do { i++; progress.innerHTML = i; } while (i % 1e3 != 0); if (i < 1e6) { + *!* queueMicrotask(count); + */!* } } @@ -300,7 +303,7 @@ alert("code"); ## 总结 -更具体的事件循环的算法(尽管跟[标准](https://html.spec.whatwg.org/multipage/webappapis.html#event-loop-processing-model)相比是简化过的): +事件循环的更详细的算法(尽管与 [规范](https://html.spec.whatwg.org/multipage/webappapis.html#event-loop-processing-model) 相比仍然是简化过的): 1. 从**宏任务**队列出列并执行最前面的任务(比如“script”)。 2. 执行所有的**微任务**: From e4b72be866b4a9db626ec0daa20cf0d4f70d79a5 Mon Sep 17 00:00:00 2001 From: LeviDing Date: Sun, 5 Apr 2020 11:38:44 +0800 Subject: [PATCH 11/11] Update article.md --- 2-ui/99-ui-misc/03-event-loop/article.md | 54 ++++++++++++------------ 1 file changed, 27 insertions(+), 27 deletions(-) diff --git a/2-ui/99-ui-misc/03-event-loop/article.md b/2-ui/99-ui-misc/03-event-loop/article.md index 2647c4a5b0..dae3c99033 100644 --- a/2-ui/99-ui-misc/03-event-loop/article.md +++ b/2-ui/99-ui-misc/03-event-loop/article.md @@ -17,13 +17,13 @@ - 从最先进入的任务开始执行。 2. 休眠直到出现任务,然后转到第 1 步。 -当我们浏览一个网页时就是上述这种形式。JavaScript 引擎大多数时候不执行任何操作,仅在脚本/处理程序/事件激活时运行。 +当我们浏览一个网页时就是上述这种形式。JavaScript 引擎大多数时候不执行任何操作,仅在脚本/处理程序/事件激活时执行。 任务示例: - 当外部脚本 `