Shu*_*tsu 10 javascript asynchronous javascript-events promise requestanimationframe
浏览器读取并运行JavaScript文件,文件中写入的同步任务立即变为执行中任务,setTimeout回调变为macrotasks,并且promise回调变为微任务.一切都是好的.
在我见面之前,我以为我掌握了JavaScript事件循环requestAnimationFrame
.
@TJ 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);
}
Run Code Online (Sandbox Code Playgroud)
规范没有说明在完成宏任务和处理其预定的微任务之间是否会发生这种情况,或者只是在macrotasks之间.所以大概可以改变浏览器到浏览器.
但在Chrome和Firefox中,微筹码总是在requestAnimationFrame
回调之前执行.我在下面的问题基于这一观察.
**Q1:**
即使浏览器没有重绘工作,requestAnimationFrame的回调是否会以刷新率执行(默认为每秒60次)?
**Q2:**
以下内容来自https://developers.google.com/web/fundamentals/performance/rendering/debounce-your-input-handlers
保证JavaScript将在帧开始运行的唯一方法是使用
requestAnimationFrame
.
太重的中间执行任务将滞后于浏览器,导致帧间隔超过16.66ms,阻止帧完成.
"保证"这个词是否意味着微任务将立即执行,当前的JS堆栈变为空,从而阻止当前帧完成(如果微任务也太重)?
这基本上是自己的事情。当浏览器即将重新绘制页面时,如果没有被正在运行的任务阻止,它通常每秒执行60次,它将requestAnimationFrame
在此之前调用所有排队的回调,然后重新绘制。
规范没有说明这是否可以在宏任务完成和计划的微任务处理之间发生,还是只能在宏任务之间发生。因此,可能会因浏览器而异。
旧的规范(现在已过时且已被取代)以宏任务的术语进行了描述,暗示它应该在宏任务之间,但是事情可能从那里继续发展。
让我们做一个测试:
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);
}
Run Code Online (Sandbox Code Playgroud)
当然,结果因浏览器而异:
(在这些浏览器上进行反复测试后,我可靠地获得了相同的结果。我没有Edge的方便...)
这是一个版本,它的忙碌等待在前面,而不是在最后,以防万一。
const messages = [];
setTimeout(() => {
// 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) {
}
// 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);
}, 100);
function log(msg) {
messages.push(Date.now() + ": " + msg);
}
Run Code Online (Sandbox Code Playgroud)
有了这一更改,我在Chrome和Firefox上都可靠地获得了微任务,requestAnimationFrame,宏任务。
**第一季度:**
即使浏览器没有重绘工作,
requestAnimationFrame
也会以刷新率(默认为每秒60次)执行的回调。
只要没有阻碍。
**第二季度:**
该句子仅表示其意思:requestAnimationFrame
绘制框架之前,将立即调用您的回调(以及排队的所有回调)。这并不意味着必须每60秒绘制一帧,因为线程可能正在忙于做其他事情。
这些回调不会中断其他任务。再次:如果其他任务的主UI线程处于繁忙状态,则该线程处于繁忙状态,并且帧速率受损。