在第一部分中,我们构建了共享的精神模型:调用堆栈,微任务队列,宏任务队列,以及微任务在下个宏任务运行前完全排出的规则.
这个模型是Spec-level JavaScript 行为——但一旦你实际运行代码,它就不是整个故事.
事件循环不是JS语言谱的一部分.
它是主机环境——浏览器或节点——的一部分,每个主机都在共享核心周围以不同的方式执行.
这是最"事件循环"的解说者跳过的文章,因为它意味着要经过图表,进入每个运行时间的实际构建方式.
浏览器: 事件循环满足渲染 在浏览器中,事件回路不仅仅是杂耍回调——它也负责保持页面的视觉响应.
这意味着渲染也必须得到一个转子,浏览器必须决定何时.
以下是每个循环迭代的大致准确的序列: 执行一个宏任务(点击处理器,调用回调器,网络事件,无论队列中下一步) 排出整个微任务队列 可能要制作一个框架 – 浏览器在每次任务完成后都不会渲染; 它会尝试点击~60fps, 并将工作在油漆之间进行分批 回到第1步"可能渲染"部分是两个API进来的地方,在Node根本不存在: ——在下一次再油漆之前,安排一个回调运行的时间.
它不是队列意义上的宏任务或微任务——直接与渲染管相连.
用于任何可视化(动画,DOM测量)取而代之,因为它被同步到浏览器实际上即将作画的时候,而不是任意的延迟. ——在浏览器闲置时,在布局和颜料后安排回调运行,并有截止时间.
对于低优先级的工作,你不希望与渲染竞争——分析,预取,非紧急DOM更新.
以下是关键互动,容易错过: 微任务可以饿死渲染.
如果一个承诺链持续排队更多的微任务,浏览器无法到达颜料步骤,因为微任务总是在渲染得到转弯之前排出全部排出.
这是一个真实的,可调试的性能bug——一个出行的链条可以明显地冻结一页,尽管在传统的同步-loop意义上"没有东西阻挡了主线程".
与此相提并论——由于每次迭代都是单独的宏任务,浏览器在它们之间有机会渲染.
节点:没有渲染,但更有条理的回路节点并不能使任何东西被渲染出,所以它不需要"可能作画"的逻辑.
取而代之,它建立在libuv上,一个C库,赋予了Node它的事件循环,线程池,Aync I/O. libuv将循环组织到不同的阶段,每个阶段都有自己的FIFO队列回调.
这与浏览器的单任务队列有着有意义的不同形状.
按顺序,每个循环勾选:定时器 ——运行由 / 其阈值已过等待调回的回调 —— 执行先前循环迭代所推迟的 I/O 调回(一些系统级的 TCP 错误等) 闲置,准备 —— 内部使用只民意调查 —— 大一:检索新的 I/O 事件,执行 I/O 相关的回调(几乎全部——文件已读取,网络请求).
节点会在这里屏蔽等待新的事件,如果没有其他预定的检查——这里运行的回调,特别是在民意调查后关闭回调——例如.
然后它循环回定时器。
在每一个单调回调之间——不仅在相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相相 与浏览器相同的排水规则, 只是应用在更精细的谷物上, 因为没有渲染 与互通。 —— 说 节点的队列-jumper节点有一个完全不存在于浏览器中的队列:.
尽管有这个名字,它不会为事件循环的"下一勾"排队——它运行在微任务之前,在当前操作完成之后,无论怎样.
在节点中优先排序, 在任何同步代码完成后: 队列( 完全排出) 保证微任务队列( 完全排出) 下一个宏任务/阶段召回 输出:,,,,,每次击败承诺,是