requestAnimationFrame何时执行?

问题描述 投票:13回答:1

浏览器读取并运行一个JavaScript文件,写入该文件的同步任务立即变为执行中任务,setTimeout回调变为宏任务,并保证回调变为微任务。一切都很好。

我以为我掌握了JavaScript事件循环,直到遇到requestAnimationFrame

@ T.J。 Crowder为我提供了以下代码段。

const messages = [];
setTimeout(() => {
  // Schedule a microtask
  Promise.resolve().then(() => {
    log("microtask");
  });
  
  // Schedule animation frame callback
  requestAnimationFrame(() => {
    log("requestAnimationFrame");
  });
  
  // Schedule a macrotask
  setTimeout(() => {
    log("macrotask");
  }, 0);
  
  // Schedule a callback to dump the messages
  setTimeout(() => {
    messages.forEach(msg => {
      console.log(msg);
    });
  }, 200);

  // Busy-wait for a 10th of a second; the browser will be eager to repaint when this task completes
  const stop = Date.now() + 100;
  while (Date.now() < stop) {
  }
  
}, 100);

function log(msg) {
  messages.push(Date.now() + ": " + msg);
}
  • Chrome:微任务,requestAnimationFrame,宏任务
  • Firefox:微任务,宏任务,requestAnimationFrame

规范没有说明这是否可以在宏任务完成与计划微任务处理之间发生,还是只能在宏任务之间发生。因此,可能会因浏览器而异。

但是在Chrome和Firefox中,微任务总是在requestAnimationFrame回调之前执行。我的以下问题基于此观察。


** Q1:**

即使浏览器没有重画工作,requestAnimationFrame的回调是否将以刷新率(默认为每秒60个)执行?


** Q2:**

下面来自https://developers.google.com/web/fundamentals/performance/rendering/debounce-your-input-handlers

确保JavaScript在帧的开头运行的唯一方法是使用requestAnimationFrame

过于繁重的中间执行任务将滞后于浏览器,导致帧间隔超过16.66ms,阻止帧完成。

'保证'一词是否意味着微任务将在当前JS堆栈变空后立即执行中,从而阻止当前帧完成(如果微任务也太重)?

javascript asynchronous javascript-events promise requestanimationframe
1个回答
10
投票

The spec并没有说这是否可以在宏任务完成与计划微任务处理之间发生,还是仅在宏任务之间发生。因此大概会因浏览器而异。

[The old spec(现在已过时且已取代)用宏任务的术语描述了它,暗示它应该在宏任务之间,但是事情可能已经从那里移了。

规范现在在Event Loop Processing Model部分中说明了何时发生这种情况。删除了大量细节的简化版本是:

执行最早的任务(宏任务)。

    执行微任务。
  1. 如果这是渲染的好时机
  2. 做一些准备工作
    1. 运行requestAnimationFrame回调
    2. 渲染
  • 让我们进行测试:
  • const messages = []; setTimeout(() => { // Schedule a microtask Promise.resolve().then(() => { log("microtask"); }); // Schedule animation frame callback requestAnimationFrame(() => { log("requestAnimationFrame"); }); // Schedule a macrotask setTimeout(() => { log("macrotask"); }, 0); // Schedule a callback to dump the messages setTimeout(() => { messages.forEach(msg => { console.log(msg); }); }, 200); // Busy-wait for a 10th of a second; the browser will be eager to repaint when this task completes const stop = Date.now() + 100; while (Date.now() < stop) { } }, 100); function log(msg) { messages.push(Date.now() + ": " + msg); }
  • © www.soinside.com 2019 - 2024. All rights reserved.