setInterval的缺点(每个setTimeout产生的任务会直接push到任务队列中;而setInterval在每次把任务push到任务队列前,都要进行一下判断(看上次的任务是否仍在队列中)。因而我们一般用setTimeout模拟setInterval,来规避掉上面的缺点)
- 某些间隔会被跳过(如果上一次执行代码没有执行,那么这次的执行代码将不会被放入队列,会被跳过)
- 可能多个定时器会连续执行(上一次代码在队列中等待还没有开始执行,然后定时器又添加第二次代码,第一次代码等待时间和执行时间刚好等于第二次代码执行)
通过setTimeout模拟setInterval,规避缺点
// setTimeout模拟setInterval
function mySetInterval(fn, timeout) {// 控制器,控制定时器是否继续执行var timer = {flag: true,};// 设置递归函数,模拟定时器执行。function interval() {if (timer.flag) {fn();setTimeout(interval, timeout);}}// 启动定时器setTimeout(interval, timeout);// 返回控制器return timer;
}
// setInterval 模拟实现 setTimeout
const mySetTimeout = (fn, t) => {const timer = setInterval(() => {clearInterval(timer);fn();}, t);
};
requestAnimationFrame实现setInterval和setTimeout
function mySetInterval(fn, time) {let timer;const now = Date.now;let startTime = now();let endTime = startTime;const loop = () => {timer = window.requestAnimationFrame(loop);endTime = now();if (endTime - startTime >= time) {startTime = endTime = now();fn(timer)}}timer = window.requestAnimationFrame(loop);return timer;
}function mySetTimeout(fn, time) {let timer;const now = Date.now;let startTime = now();let endTime = startTime;const loop = () => {timer = window.requestAnimationFrame(loop);endTime = now();if (endTime - startTime >= time) {startTime = endTime = now();fn()window.cancelAnimationFrame(timer)}}timer = window.requestAnimationFrame(loop);return timer;
}let a = 0;
mySetInterval((timer) => {console.log(a++);if (a === 3) {window.cancelAnimationFrame(timer)}
}, 1000)