Event Loop事件循环机制

JavaScript在浏览器里的执行流程跟在Node.js中同样,是基于事件循环的。javascript

事件循环:一个在JavaScript引擎等待任务、执行任务和休眠等待更多任务这几个状态之间的无穷无尽的循环。java

宏任务

执行引擎通用的算法:算法

  1. 当有任务时从最早进入的任务开始执行
  2. 休眠到有新的任务进入,而后到第 1 步 浏览网页时,JavaScript引擎大多数时候什么也不作,只在一个脚本、处理函数或者事件被激活时运行。

一个任务到来时引擎可能正处于运行状态,那么这个任务就被入队。多个任务组成了一个队列,命名为“宏任务队列”(v8 术语),引擎按照先进先出来处理它们,而后等待更多的任务(即休眠,几乎不消耗 CPU 资源)。promise

宏任务举例:浏览器

  • 当外部脚本 <script src="..."> 加载进来时,任务就是执行它。
  • 当用户移动鼠标时,任务就是派发出 mousemove 事件和执行处理函数。
  • 当定时器 setTimeout 到期时,任务就是运行其回调。

当引擎忙于执行一段 script 时,还可能有用户移动鼠标产生了 mousemove 事件,setTimeout 或许也恰好到期等这些事件,这些任务组成一个队列:markdown

当引擎处理任务时不会执行渲染,对于 DOM 的修改只有当任务执行完成才会被绘制。 若是一个任务执行时间过长,浏览器没法处理其余任务,在必定时间后就会在整个页面抛出一个如“页面未响应”的警示建议终止这个任务。这样的场景常常发生在不少复杂计算或者程序错误执行到死循环里。网络

使用 0 延时的 setTimeout(f)来计划一个新的宏任。 它被用来拆分一个计算耗费型任务为小片断,使浏览器能够对用户行为做出反馈和展现计算的进度。 也被用在事件处理函数中来定时执行一个行为,在当前事件被彻底处理(冒泡结束)以后。异步

微任务

微任务仅仅由咱们的代码产生。它们一般由 promises 生成:对于 .then/catch/finally 的处理函数变成了一个微任务。微任务一般"隐藏在" await 下,由于它也是另外一种处理 promise 的形式。函数

一个宏任务结束后,先执行全部微任务队列中的任务,而后再去执行其余宏任务或渲染。微任务优先级高保证了微任务中的程序运行环境基本一致(没有鼠标位置改变,没有新的网络返回数据,等等)。 有一个特殊的函数 queueMicrotask(func),能够将 func 加入到微任务队列来执行。若是咱们想要异步执行(在当前代码以后)一个函数,可是要在修改被渲染或者新的事件被处理以前,咱们能够用 queueMicrotask 来定时执行。spa

setTimeout(() => alert("timeout"));
Promise.resolve()
  .then(() => alert("promise"));
alert("code");
// code -> promise -> timeout
复制代码

更具体的事件循环的算法:

  1. 从宏任务队列出列并执行最前面的任务(好比“script”)。
  2. 执行全部的微任务:
  3. 当微任务队列非空时:出列并运行最前面的微任务。
  4. 若有须要执行渲染。
  5. 若是宏任务队列为空,休眠直到一个宏任务出现。
  6. 到步骤 1 中。

Promise与微任务

Promise 的处理程序(handlers).then、.catch 和 .finally 都是异步的。 异步任务须要适当的管理。为此,JavaScript 标准规定了一个内部队列 PromiseJobs —— “微任务队列”(Microtasks queue)(v8 术语)。 这个队列先进先出,只有引擎中没有其余任务运行时才会启动任务队列的执行。 当一个 promise 准备就绪时,它的 .then/catch/finally 处理程序就被放入队列中。等到当前代码执行完而且以前排好队的处理程序都完成时,JavaScript引擎会从队列中获取这些任务并执行。 即使一个 promise 当即被 resolve,.then、.catch 和 .finally 以后的代码也会先执行。 若是要确保一段代码在 .then/catch/finally 以后被执行,最好将它添加到 .then 的链式调用中。

let promise = Promise.resolve();

promise.then(() => alert("promise done"));

alert("code finished"); // 该警告框会首先弹出
复制代码
未处理的 rejection

指在 microtask 队列结束时未处理的 promise 错误。 microtask队列完成时,引擎会检查promise,若是其中任何一个出现rejected状态,就会触发unhandledrejection事件。 但若是在setTimeout里进行catch,unhandledrejection会先触发,而后catch才执行,因此catch没有发挥做用。

let promise = Promise.reject(new Error("Promise Failed!"));
setTimeout(() => promise.catch(err => alert('caught')));
window.addEventListener('unhandledrejection', event => alert(event.reason));
// Promise Failed! -> caught
复制代码
相关文章
相关标签/搜索