【前端怪谈】为什么要用setTimeout模拟setInterval

news/2024/7/19 16:32:16 标签: js, 前端

文章目录

  • 前言
  • 一、setInterval存在的问题
    • 1.问题复现
    • 2.问题分析
  • 二、setInterval缺点及setTimeout
    • 1.setInterval缺点
    • 2.为什么setTimeout能取代
    • setTimeout实现setInterval
  • 总结


前言

大家都知道,setTimeout是延迟执行函数,而setInterval就像一个定时器,每间隔一定时间就触发一次回调。

今日面试中,面试官问了我一个问题,为什么要用setTimeout去实现setInterval呢?

在这里插入图片描述
再一次被自己菜哭,面试完成后,赶紧去查了查为什么,下面让我们一起看一看setInterval会导致些什么问题吧!

一、setInterval存在的问题

1.问题复现

让我们先来看下面这一串代码:

      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);

我们在打印之前,增加一个setInterval,来看一看有什么影响
在这里插入图片描述
我们可以看到,相差的毫秒数变得非常大并且还在不停的增加!(网页一打开,电脑风扇都开始疯狂运动了!)

结案:setInterval一定有问题!

2.问题分析

setInterval(fn,time)

上面这串代码的意思是:fn在每隔time毫秒之后被推入任务队列

注意:不是直接放入任务队列

setInterval 被推入任务队列时,如果在它前面有很多任务或者某个任务等待时间较长比如网络请求等,那么这个定时器的执行时间和我们预定它执行的时间可能并不一致。

意味着,如果我们定时器里面的代码需要进行大量的计算,花费的时间就会比较长,那么我上一次的代码还没有执行完毕,那么我又推入了下一次的代码去任务队列,这个时候就会变得不准确。

二、setInterval缺点及setTimeout

1.setInterval缺点

根据上文我们得知,setInterval是存在以下缺点的:

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

通俗来说:每个 setTimeout 产生的任务会直接 push 到任务队列中;而 setInterval 在每次把任务 push 到任务队列前,都要进行一下判断(看上次的任务是否仍在队列中,如果有则不添加,没有则添加)。

2.为什么setTimeout能取代

	  for (let i = 0; i < 5; i++) {
        setTimeout(() => {
          console.log(i);
        }, 1000);
      }

我们看一下这串代码,大家都知道,for里面的宏任务是会被推迟到for循环完成后执行的。(什么?你不知道?赶紧恶补时间队列知识)

广告:想让for循环里面的异步操作同步执行该怎么做呢?传送门:浅谈Generator和yield

在这里插入图片描述
我们打印结果,发现5个结果是同时出来的,这意味着什么?

没错,多个setTimeout并不会互相影响!

所以,我们利用setTimeout模拟setInterval,就能解决掉这个问题!

setTimeout实现setInterval

	  const myInterval = (fn, time) => {
        const timeout = () => {
          setTimeout(() => {
            fn();
            timeout();
          }, time);
        };
        timeout();
      };
	  const test = () => {
        console.log("111");
      };
      myInterval(test, 1000);

在这里插入图片描述
成功啦!

总结

以上就是今天要讲的内容,前端怪谈将带你一起探索前端中的"怪事"。


http://www.niftyadmin.cn/n/861664.html

相关文章

深入透析Promise实现细节(含手撕阶段)

文章目录前言一、Promise是什么&#xff1f;二、Promise核心逻辑实现1.基本原理2.新建类promise类&#xff0c;传入执行器executor3.传入resolve和reject方法4.then方法的简单实现5.完整代码及验证6.代码改进三.链式调用1.链式调用实现的基本思路2.then方法返回promise对象3.re…

闭包真的还会造成内存泄露吗?你不知道的闭包与垃圾回收!

文章目录前言一、闭包是什么&#xff1f;二、闭包有什么好处和坏处呢&#xff1f;1.好处2.坏处二、闭包会造成内存泄露吗&#xff1f;1.前言2.闭包会造成内存泄露吗&#xff0c;如果会为什么还会再react hooks中大量使用呢&#xff1f;三、为什么ie8及之前会造成内存泄露&#…

【JS垃圾回收】带你探索垃圾回收机制和Chrome V8垃圾回收机制

文章目录前言一、什么是垃圾回收&#xff1f;1.基本思路2.为什么要进行垃圾回收二、怎样进行垃圾回收1.标记清除优点缺点2.引用计数优点缺点三、Chrome V8垃圾回收机制1.为什么需要优化垃圾回收算法2.基本概念3.新生代垃圾回收器 - Scavenge4.老生代垃圾回收 - Mark-Sweep &…

深入透析Promise几种方法(含手撕思路讲解及坑点)

文章目录前言一、Promise.all()1.介绍2.实例状态全为fulfilled状态有一个为rejected3.代码实现思路代码二、Promise.race()1.介绍2.实例3.代码实现思路代码总结前言 前面我们简单实现了一个promise&#xff0c;不懂的同学&#xff0c;传送门&#xff1a;深入透析Promise 那么…

【Diff算法图解】带你探索React、Vue2.x的Diff算法

文章目录前言一、Virtual DOM&#xff08;虚拟dom&#xff09;二、React Diff实现思想移动节点增加节点移除节点React Diff的缺陷三、Vue2.X Diff实现思想移动节点特殊情况增加节点移除节点总结前言 我们都知道&#xff0c;在框架中&#xff0c;当dom节点发生变化时&#xff0…

CSS初学的简单样式

CSS导航条 <!DOCTYPE html> <html><head><meta charset"UTF-8"><title></title><style type"text/css">*{margin: 0;padding: 0;}.nav{list-style: none;background-color: blueviolet;width: 500px;margin:50p…

CSS风车

CSS 风车 添加边框后不改变原本设计的大小&#xff1a; Box-sizing:border-box&#xff1b;<!DOCTYPE html> <html><head><meta charset"UTF-8"><title></title><style type"text/css">*{margin: 0;padding: 0…

ES6中的方法-1

ES6 的方法 1、let关键字* 在块级作用于有效* 不能重复声明* 不会预处理&#xff0c;不存在变量提升2、const关键字* 不能修改* 其他等同let3、变量解构赋值例&#xff1a;*对象解构let obj{username:kobe,age:39}let {username,age}objconsole.log(username)*数组解构let arr[…