浏览器的宏任务与微任务

浏览器的宏任务与微任务

本文写于 2022 年 07 月 22 日

什么是宏任务、微任务

JavaScript 中用来存储待执行的回调函数的队列包含 2 个不同特定的列队:

  • 宏列队:用来保存待执行的宏任务
    • 定时器回调 / DOM 事件回调 / AJAX 回调(fetch 基于 Promise,所以是微任务)
  • 微列队:用来保存待执行的微任务
    • Promise 的回调 / MutationObserver 的回调

执行顺序

  1. 首先必须先执行所有的初始化同步任务
  2. 然后会检查微任务队列,并将其清空
  3. 接着取出第一个宏任务
  4. 当该宏任务执行完成,会检查其中的微任务队列,如果为空则直接执行下一个宏任务;如果不为空,则依次执行微任务,执行完成才去执行下一个宏任务

举个例子:

setTimeout(() => console.log(3));
Promise.resolve().then(() => console.log(2));
console.log(1);

这段代码会依次输出 1、2、3,应该能很好的体现出上面所说的几点。

关于第四点可能有些复杂,我们用一个复杂点的例子来说明:

setTimeout(() => {
  console.log(3);
  Promise.resolve().then(() => console.log(4));
});
setTimeout(() => console.log(5));
Promise.resolve().then(() => console.log(2));
console.log(1);

该例子也会顺序输出:1、2、3、4、5。

任务的间隙能进行页面渲染吗?

我们先忽略第一次清空微任务队列的流程,浏览器的事件循环大体如下:

宏任务 * 1 => 微任务清空 => 页面渲染

可以看到,在每一个宏任务之后,会尝试清空微任务队列,之后便会进行页面渲染。

这个时候我们思考一个问题:任务过多会影响页面渲染吗?

如果假设单个任务的耗时都不长,那么:

  1. 如果宏任务过多,完全不会影响页面渲染
  2. 如果微任务过多,会影响到页面渲染

但是,我们可以尝试往微任务队列塞很多任务,发现好像并没有卡死我们的页面,只是稍微卡顿了一点点而已。

这是因为浏览器也考虑到完全卡死的可能性,所以限制了单次微任务执行的最大次数,默认为 1000 次。

(完)

posted @ 2022-07-22 11:09  徐航宇  阅读(491)  评论(0编辑  收藏  举报