setTimeout实现setInterval

一、setInterval存在的问题

直接例子:

      let startTime = new Date().getTime();
      let count = 0;
      setInterval(function () {
    
    
        count++;
        console.log(
          `与原设定的间隔时差了${
      
      
            new Date().getTime() - (startTime + count * 1000)
          }毫秒`
        );
      }, 1000);

在这里插入图片描述
在执行中可能会造成一点时间误差,但是可以忽略不计。

然而,我们把代码改一改:

	  let startTime = new Date().getTime();
      let count = 0;
      //耗时任务
      setInterval(function () {
    
    
        let i = 0;
        while (i++ < 1000000000);
      }, 0);
      setInterval(function () {
    
    
        count++;
        console.log(
          `与原设定的间隔时差了${
      
      
            new Date().getTime() - (startTime + count * 1000)
          }
          毫秒`
        );
      }, 1000);

在这里插入图片描述
我们可以看到,相差的毫秒数变得非常大并且还在不停的增加,这个误差是不可接受的。

那是什么原因造成了这个问题呢?
原因⚠️:是在EventLoop中,setInterval的执行机制造成的。
通俗来说:每个 setTimeout 产生的任务会直接 push 到任务队列中;而 setInterval 在每次把任务 push 到任务队列前,都要进行一下判断(看上次的任务是否仍在队列中,如果有则不添加,没有则添加)。

setInterval缺点,也就显而易见了:

  1. 使用setInterval时,某些间隔会被跳过(如果上一次执行代码没有执行,那么这次的执行代码将不会被放入队列,会被跳过)
  2. 可能多个定时器会连续执行(上一次代码在队列中等待还没有开始执行,然后定时器又添加第二次代码,第一次代码等待时间和执行时间刚好等于第二次代码执行)

二、setTimeout实现SetInterval

	const myInterval = (fn, time) => {
    
    
		// 定义一个递归函数持续调用定时器
		let executor = (fn, time) => {
    
    
			timer[key] = setTimeout(() => {
    
    
				fn();
				executor(fn, time);
			}, time)
		}
		executor(fn, time);
	}

测试:myInterval(()=>{console.log(111)},3000);
在这里插入图片描述
测试结果没有问题,那我们如何实现类似于clearSetInterval方法,清除计时器呢?
先来研究一下setTimeOut
在这里插入图片描述
setTimeout 返回的竟然是一串整数,并且这些整数都不重复,还是连续的,没错,这个就是clearTimeout 工作的时候,会找到对应的id的定时器,然后清除掉,好了,知道了这个,那么我们能不能把当前定义的mySetInterval id存储下来呢?

var timeWorker = {
    
    }
var mySetInterval= function(fn, time) {
    
    
// 定义一个key,来标识此定时器
var key = Symbol();
// 定义一个递归函数,持续调用定时器
  var execute = function(fn, time) {
    
    
     timeWorker[key] = setTimeout(function(){
    
    
        fn();
        execute(fn, time);
     }, time)
   }
  execute(fn, time);
  // 返回key
  return key;
}
var myClearInterval = function(key) {
    
    
  if (key in timeWorker) {
    
    
   clearTimeout(timeWorker[key]);
    delete timeWorker[key];
  }
}

测试一下:

	let time1 = mySetInterval(() => {
    
    console.log(111)}, 3000);
	let time2 = mySetInterval(() => {
    
    console.log(222)}, 3000);

	setTimeout(() => {
    
    
		myClearInterval(time2);
	}, 4000)

在这里插入图片描述
实现完成✌️!

猜你喜欢

转载自blog.csdn.net/weixin_44761091/article/details/123983972