时间管力大师就是要忙里偷闲
各位应该知道 JavaScript 是单执行绪(单线程)的程序语言,也就是一次只能处理一件事情。这样的特性会使得事件的执行必定有个先後顺序,这时候就会希望重要的事情能够排序在前面,剩下比较不重要的任务等空闲时再处理即可,这时候就可以靠 RequestIdleCallback 来帮助我们。
RequestIdleCallback 会在浏览器「每一帧」中剩下的空闲里来执行当中的 Callback。
我们之前在介绍 RequestAnimationFrame 时有提过「帧数(FPS)」的概念,也就是「一秒钟内能够更新多少帧」,假如在一秒内能够更新 60 帧,则 FPS 为 60,每一帧的时间约为 16.7 ms(毫秒)。
对於浏览器来说每一次「重绘(Repaint)」就是「一帧」,而这一帧要花多少时间就要看当下的网路或硬体状况而定了。在这每一帧中,浏览器都有可能正在执行任务,若这个任务完成时,当下那一帧还没结束时,就会有一个短暂的空闲时间。
以 60FPS 为例,每一帧的空闲时间必定小於等於 16.7 ms。
而只要有这个空闲时间 RequestIdleCallback 就会去执行当中的 Callback,来完成那些我们觉得不重要的任务,换句话说,如果浏览器一直处於繁忙状态的话,那该任务就会一直无法执行。
requestIdleCallback
有两个参数要传入:
callback
,以避免浏览器因为持续繁忙的忽略(单位:毫秒)。大部分情况不建议使用
timeout
,因为会使用requestIdleCallback
就代表不想影响主线程的任务进行 。
const handlerId = requestIdleCallback(function () {
//..做些不重要的事
}, 500);
cancelIdleCallback(handlerId); // 取消requestIdleCallback
而我们传入的 Callback Function 会被丢进一个由 requestIdleCallback
提供的参数,该参数通常取名为 deadline
,并且有两个属性可以使用:
timeout
被触发的。requestIdleCallback(function (deadline) {
// 如果你在 requestIdleCallback 中没有传入 timeout 参数,didTimeout 必定为 false
console.log(deadline.didTimeout);
console.log(deadline.timeRemaining());
}, 500);
由於 JavaScript 是单执行绪,所以要是我今天进行了一个需要耗费大量时间的任务,那使用者的 UI 操作其实也会受到影响。
就像下面这个范例中,在 count
被函式 add
加到 1000000 以前,你不管怎麽敲击键盘,keydown
事件都不会被触发,因为浏览器正在忙着算数:
window.addEventListener("keydown", function () {
console.log("Hey !!!!!!!!!");
});
let count = 0;
add();
function add() {
if (count < 10000) {
console.log(count++);
add();
}
}
但是我们只要用 requestIdleCallback
来改写一下,那状况就不一样了,因为这时候 add
这项任务的优先度会往後排,所以当我按下键盘时,浏览器会先处理 keydown
事件,等到闲置下来後才会继续进行。
window.addEventListener("keydown", function () {
console.log("Hey !!!!!!!!!");
});
let count = 0;
requestIdleCallback(add);
function add(deadline) {
if (deadline.timeRemaining() > 0) {
if (count < 10000) {
console.log(count++);
requestIdleCallback(add);
}
}
}
在了解 RequestIdleCallback 的效果後,我第一个想到的实际应用会是 LazyLoad,想像以下,如果我们有个网页,当中有几十甚至几百张的高画质图片需要显示,可想而知浏览器的负担会相当的大,非常有可能会影响页面的效能与任务执行,但如果们我利用 requestIdleCallback
来处理,就可以在不影响主执行绪的情况下载入图片。
const images = [
"https://img/001.png",
"https://img/002.png",
//.....
"https://img/099.png",
"https://img/100.png",
];
requestIdleCallback(loadImage);
function loadImage(deadline) {
if (deadline.timeRemaining() > 0) {
if (images.length) {
const imgSrc = images.shift();
const img = new Image(250, 150);
img.onload = document.body.appendChild(img);
img.src = imgSrc;
requestIdleCallback(loadImage);
}
}
}
不晓得使用过 React 的朋友有没有了解过 React Fiber 呢?其实它的原理就和 RequestIdleCallback 一样,将大量没那麽优先的工作拆成许多小片段,在琐碎的时间里慢慢完成,也因为这样的机制,使得我们可以去中断它,将一些突发的重要任务(例如使用者的 UI 事件)插在这些小片段中,宛如有另一条执行绪一般。
>>: [Day18] Null byte Injection
「子非鱼,安知鱼之乐。」 在介绍协定之前, 我们要来介绍一个非常重要的概念,叫做凭证颁发机构(Cer...
记得初学Java的时候,若要对List进行排序,可以使用Collections的静态方法sort()...
我们已经顺利的将 onEdit(e) 以及 MailApp.sendMail(message) 学完...
使用 atlantis 做 terraform automation,Terraform Remot...
已经先有测试资料了 来试试看删除文件的方法 doc_info/views.py 一样使用修饰器来验证...