题目
常常运用Javascript的同砚肯定对setInterval
异常熟习,当运用setInterval(callback, timer)
时,每经由timer毫秒时候,体系都将挪用一次callback。叨教全局假如没有供应setInterval函数,该怎样本身完成这一功用?
计划一:轮回或递归(毛病解法)
最简朴的思绪就是经由过程简朴的轮回或许递归,每次搜检时候戳是不是已凌驾上次触发给定函数的时候加上间隔时候,假如已凌驾便再次触发函数,并重置计时器至当前时候。
const setInterval1 = (func, interval) => {
let startTime = Date.now();
const config = { shouldStop: false };
while (!config.shouldStop) {
if (Date.now() - startTime >= interval) {
func();
startTime = Date.now();
}
}
return config;
}
const myClearInterval = config => { config.shouldStop = true; }
然则如许的解法有一个致命题目,我们将setInterval1变成一个壅塞函数,主线程会卡死在这个无穷轮回或许递归中,致使以后的代码或许事宜没法实行。想相识细致缘由的请戳: 并发模子与事宜轮回,JavaScript:完全明白同步、异步和事宜轮回(Event Loop)
计划二:运用setTimeout
setTimeout的优点在于,它是在音讯行列内里增加一个待实行的音讯,所以并不会梗塞主线程。更轻易的在于,由于setTimeout自带定时器功用,我们以至不必本身去保护一个时候戳。我们能够经由过程不停递归挪用setTimeout来完成setInterval的结果
const setInterval2 = (func, interval) => {
const config = { shouldStop: false }
const loop = () => {
if (!config.shouldStop) {
func();
setTimeout(loop, interval);
}
}
setTimeout(loop, interval);
return config;
}
const myClearInterval = config => { config.shouldStop = true; }
计划三:运用requestAnimationFrame
然则运用setTimeout
有违这道题的初志,由于setTimeout
在本质上和setInterval
是相似的,若干有些做弊的怀疑。那有没有别的非壅塞计划呢?在浏览器环境中,我们有requestAnimationFrame()
,而在nodejs环境中,我们有setImmediate()
。以requestAnimationFrame为例,这将保证我们的代码只会在每一帧render之前被递归一次,从而避免了壅塞其他代码。
const setInterval3 = (func, interval) => {
let startTime = Date.now();
const config = { shouldStop: false }
const check = () => {
if (!config.shouldStop) {
if (Date.now() - startTime > interval) {
func();
startTime = Date.now();
}
if(typeof window === 'undefined') {
setImmediate(check);
} else {
window.requestAnimationFrame(check)
}
}
}
check();
return config;
}
const myClearInterval = config => { config.shouldStop = true; }
计划四:运用Web Worker
requestAnimationFrame能确保我们在每帧显现前被挪用一次,从而检计时器是不是到期,然则假如被实行的函数盘算量极大,致使帧内没法完成时,该怎样保证给定函数能定时实行呢?明显,此时只依托主线程来确保计时顺序和给定顺序都能正确实行,有点难题,然则假如将计时顺序放入另一线程中,而主顺序只担任监听定时器事宜和实行给定顺序,是不是是会好一些呢?所以我们这里应用浏览器供应的Web Worker API来完成多线程。请注意这里由于没有挪用另一个剧本,我们经由过程blob和object url的体式格局将我们的定时器顺序check
传入Web Worker中。
const setInterval4 = (func, interval) => {
if (typeof window !== 'undefined' && window.Worker && window.Blob) {
const check = new Blob(["(", function(){
self.onmessage = function(e) {
const interval = e.data;
let startTime = Date.now();
while(true) {
if (Date.now() - startTime >= interval) {
startTime = Date.now();
self.postMessage(Date.now());
}
}
}
}.toString(), ")()"], { type: "text/javascript" });
const worker = new Worker(window.URL.createObjectURL(check));
worker.onmessage = func;
worker.postMessage(interval);
return worker;
} else {
console.log('Your environment is not supported');
}
}
const myClearInterval = config => { config.terminate() }