• events: 点击事件、键盘事件、滚动事件等
  • macro: 宏任务,如 setTimeout
  • micro: 微任务,如 Promise
  • rAF: requestAnimationFrame
  • Layout: CSS 计算,页面布局
  • Paint: 页面绘制
  • rIC: requestIdleCallback

RAF

RAF是RequestAnimationFrame的简称。

在制作动画的时候,我们不能预设浏览器的帧率,正确的做法是通过 rAF 注册回调, 由浏览器来控制动画调用时机:

rAF 会保证注册的回调在下次渲染页面之前执行,且只会执行一次。另外,当页面处于不可见状态时,rAF 会自动停止执行,以节省系统资源。

RIC

RIC是RequestIdleCallback的简称。

执行顺序

  • 微任务队列会在 JS 运行栈为空的时候立即执行。
  • animation 队列会在页面渲染前执行。
  • 宏任务队列优先级低于微任务队列,一般也会比 animation 队列优先级低,但不是绝对 。
  • idle 队列优先级最低,当浏览器有空闲时间的时候才会执行。
setTimeout(()=>console.log('setTimeout'), 0);
Promise.resolve().then(()=>console.log('promise'));
requestAnimationFrame(()=>console.log('animation'));
requestIdleCallback(()=>console.log('idle'));

// 执行结果大多数情况下是: promise, animation, setTimeout, idle
// 少数情况是:promise, setTimeout, animation, idle

rAF类似,rIC 的执行时机是由浏览器控制的,能更好的保证体验,优化性能。一般优先级高的任务(如 UI 更新)会放在 rAF 队列,优先级低的任务(如日志上传)会放 rIC

队列特性

在一个事件循环内,各个队列有以下特性:

  • 宏任务队列,每次只会执行队列内的一个任务。
  • 微任务队列,每次会执行队列里的全部任务。假设微任务队列内有 100 个 Promise,它们会一次过全部执行完。这种情况下极有可能会导致页面卡顿。如果在微任务执行过程中继续往微任务队列中添加任务,新添加的任务也会在当前事件循环中执行,很容易造成死循环, 如:
function loop() {
    Promise.resolve().then(loop);
}
loop();
  • animation 队列,跟微任务队列有点相似,每次会执行队列里的全部任务。但如果在执行过程中往队列中添加新的任务,新的任务不会在当前事件循环中执行,而是在下次事件循环中执行。
  • idle 队列,每次只会执行一个任务。任务完成后会检查是否还有空闲时间,有的话会继续执行下一个任务,没有则等到下次有空闲时间再执行。需要注意的是此队列中的任务也有可能阻塞页面,当空闲时间用完后任务不会主动退出。如果任务占用时间较长,一般会将任务拆分成多个阶段,执行完一个阶段后检查还有没有空闲时间,有则继续,无则注册一个新的 idle 队列任务,然后退出当前任务。
上次更新:
贡献者: chenzilin