setInterval存在的问题

1.setInterval机制:

  1. 延时一段时间后,将任务push到任务队列中排队执行;
  2. 在每次把任务 push 到任务队列前,都要进行一下判断(看上次的任务是否仍在队列中,如果有则不添加,没有则添加)。
基于该原理,会导致两个问题:
  1. 前一任务结束到当前任务开始的时间间隔与设置的delay值不符。
  2. 可能出现某些任务被跳过的情况

(1)任务添加的时间间隔误差较大的情况

setInterval(foo, delay)
//当foo执行消耗800ms,delay为1000ms时,前一个任务执行结束到后一个任务开始执行只间隔了200ms

假如定时器里面的代码需要进行大量的计算(耗费时间较长),或者是 DOM 操作。这样一来,花的时间就比较长,有可能前一次代码还没有执行完,后一次代码就被添加到队列了。也会到时定时器变得不准确。

// 无耗时较长的同步代码时:
let end = new Date().getTime();
let start = new Date().getTime();
setInterval(function () {
  start = new Date().getTime();
  console.log("上一任务结束——当前任务开始:", start - end, "ms");
  end = new Date().getTime();
}, 1000);
 
// 执行结果(与预期相符):
// 上一任务结束——当前任务开始: 1005 ms
// 上一任务结束——当前任务开始: 997 ms
// 上一任务结束——当前任务开始: 1002 ms
// 上一任务结束——当前任务开始: 998 ms
// 上一任务结束——当前任务开始: 1000 ms
// 上一任务结束——当前任务开始: 1000 ms
// 上一任务结束——当前任务开始: 1002 ms
// 上一任务结束——当前任务开始: 1000 ms

(2)可能出现某些间隔会被跳过在这里插入图片描述
上图可见,setInterval 每隔 100ms 往队列中添加一个事件;100ms 后,添加 T1 定时器代码至队列中,主线程中还有任务在执行,所以等待,some event 执行结束后执行 T1 定时器代码;又过了 100ms,T2 定时器被添加到队列中,主线程还在执行 T1 代码,所以等待;又过了 100ms,理论上又要往队列里推一个定时器代码,但由于此时 T2 还在队列中,所以 T3 不会被添加(T3 被跳过),结果就是此时被跳过;这里我们可以看到,T1 定时器执行结束后马上执行了 T2 代码,所以并没有达到定时器的效果。

没有耗时较长时间的任务时:

let startTime = new Date().getTime();
let count = 0;
setInterval(function () {
  count ++;
  //显示当前任务实际执行时间与计划执行时间的差值
  console.log(
    "与原设定的间隔时差了:",
    new Date().getTime() - (startTime + count * 1000),
    // a,
    "毫秒"
  );
}, 1000);

在这里插入图片描述
任务中存在耗时较长的操作:

let startTime = new Date().getTime();
let count = 0;
setInterval(function () {
// 耗时较长的操作
  let i = 0;
  while (i++ < 2500000000);
  count ++;
//显示当前任务实际执行时间与计划执行时间的差值
  console.log(
    // "count",
    // count,
    "与原设定的间隔时差了:",
    new Date().getTime() - (startTime + count * 1000),
    // a,
    "毫秒"
  );
}, 1000);

在这里插入图片描述

解决方案

可使用setTimeout 模拟 setInterval。

setTimeout 机制:产生的任务会直接 push 到任务队列中。
function mySetInterval(fun, delay) {
    let timer = null
    function interval() {
        //fun中的同步代码执行完之后,再开始定时
        fun()
        setTimeout(interval, delay);
    }
    interval()
    return {
        cancel: () => {
            clearTimeout(timer)
        }
    }
}
mySetInterval(() => console.log('mySetInterval'), 1000)
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值