面试题:setInterval和setTimeout

338 阅读3分钟

昨天看到了一道面试题,是下方这样:

    setInterval(() => {
    }, 1000)
    // ========================
    function fn () {
      setTimeout(() => {
        fn()
      }, 1000)
    }

这两个有什么区别。

首先呢setInterval是每一秒会产生一个任务,而setTimeout是每一秒执行一个任务。也就是这两个在异步进行执行的时候,setInterval是不管你是否执行完毕了,只要时间到了,就会从其它线程往你的任务队列里添入一个待执行任务,而setTimeout呢是,他最多只会在线程上留有一个,也就是这个任务执行完毕以后,或者是在执行的时候(因为不确定fn是写在了逻辑的后面还是前面),其它线程只有这一个,等到这个线程的把任务放入了任务列表再执行到这个任务的时候才会再存在一个。

   function sleep (timer) {
      const start = Date.now()
      //这里是一个死循环   timer决定死循环的时间
      while (Date.now() - start < timer) { }
    }
     let data = Date.now()
       setInterval(() => {
          sleep(3000)
    }, 1000)

可以看一下代码,setInterval内部逻辑需要3秒才能执行完毕,但是这个时候他一秒就会产生一个任务,到任务列表里待执行,导致任务执行速度无法跟上任务产生的速度。这会导致任务堆积在任务列表中,最终可能会导致内存耗尽或浏览器崩溃。

image.png

看图可能会更好理解一些,首先setInterval他是运行在了异步线程,但是任务执行是在主线程的,也就是他到达时间以后会把他的回调函数包装成任务给到任务队列执行,但是任务队列需要3秒钟来执行这个任务,但是他一秒产生一个,所以可能会产生任务堆积。

setTimeout只会执行一次,他是在函数内部又被调用了一次,也就是说,只有函数执行的时候,异步线程又会产生一个setTimeoutsetTimeout就会等到达时间以后送到任务队列

    function fn () {
      setTimeout(() => {
        sleep(3000)
        fn()
      }, 1000)
    }
    fn()

他堵塞三秒,三秒以后再次调用fn函数,异步线程就会产生一个setTimeout(上一个setTimeout已经不存在了,执行完毕了已经),此时才会间隔一秒才会再次执行,不会产生堆积的情况。

image.png

首先这里有一个fn的函数,在执行的时候呢产生了一个setTimeoutsetTimeout去到异步线程,等到时间到了,他会把这个任务放入到任务队列,此时任务队列拿到任务开始执行,逻辑执行了3秒,后再次调用了fn。 此刻上一个setTimeout已经不存在了,存在的是新的fn又会产生一个setTimeout在异步了等待,再合适的时间放入任务队列。

但是如果是这样呢

    function fn () {
      setTimeout(() => {
        fn()
        sleep(3000)
      }, 1000)
    }
    fn()

先调用了fn再去执行逻辑。这样首先呢会再次产生一个倒计时1秒的任务在异步线程,然后三秒循环完毕,此刻呢异步线程的倒计时1秒的任务已经在任务队列了,所以再次执行fn,fn会在往异步线程里添入一个,一直这样循环

image.png

这个上面的那个的区别就在于,最后试用fn的话时执行完毕等待一秒才再会产生一个再执行,先调用的话就执行完这个立马执行在产生一个

概况
setInterval呢是一秒产生一个任务,如果内部逻辑执行时间大于了定时器的时间,会造成任务队列堆积,setTimeout呢是一秒呢执行一个任务,他不管内部逻辑时间多长,只会在再调用以后再次产生任务,等待执行。不会造成任务的堆积