关于setTimeout & setInterval

2019-05-31  本文已影响0人  w_danboard

前言

之所以写这篇文章是因为上周工作中使用setInterval轮询请求接口时遇到了一些问题,如果哪里理解的不对请大家多多指教~

进入正题

setTimeout和setInteval是window对象上两个主要的定时方法,他们的语法基本相同,但完成功能的却是不同的。
定时器的返回值
定时器的清除

注意: 定时器需要手动清除,并且clearTimeout和clearInterval都可以清除setTimeout或setInterval,但并不建议这样做,容易造成混淆。

定时器的this指向

setTimeout和setInterval是如何工作的?

涉及到的知识点:JS事件循环机制EVENTLOOP

注意:异步任务之间并不相同,他们的执行优先级有区别。不同的异步任务会被分为两类:微任务(micro task)和宏任务(macro task)

^ ^.png

使用定时器的时候,千万不要太相信预期,延迟的时间严格来说总是大于xxx毫秒的,至于大多少就要看当时执行的情况了。即使设置为0也不会马上执行,HTM5规范定最小延迟时间不能小于4ms,不同浏览器的实现不一样,比如,Chrome可以设置1ms,IE11/Edge是4ms。

setTimeout

setTimeout注册的函数fn会交给浏览器的定时器模块来管理,延迟时间到了就将fn加入主进程执行队列,如果队列前面还有没有执行完的代码,则又需要花一点时间等待才能执行到fn,所以实际的延迟时间会比设置的长。如在fn之前正好有一个超级大循环,那延迟时间就不是一丁点了。

(function testSetTimeout() {
    console.time('timer');
    const timer = setTimeout(() => {
        console.timeEnd('timer');
    }, 10);
    for(let i = 0; i < 100000000; i++) {}
})();

// timer: 59.364990234375ms 远远不止10ms
setInterval

为什么尽量别用setInterval???

setInterval无视代码错误

setInterval有个讨厌的习惯,即对自己调用的代码是否报错这件事漠不关心,如果setInterval执行的代码由于某种原因出了错,它还会持续不断(不管不顾)地调用该代码。

function a() {
  try {
    cnosole.log('单词拼写错误,应该是console')
  } catch (e) {
    console.log('错误了')
  }
}
setInterval(a, 1000)

// 8VM69:5 错误了  (控制台每间隔一秒就会输出一个错误了) 
setInterval无视网络延迟

假设你每隔一段时间就通过Ajax轮询一次服务器,看看有没有新数据。而由于某些原因(服务器过载、临时断网、流量剧增、用户带宽受限,等等,你的请求要花的时间远比你想象的要长。但setInterval不在乎。它仍然会按定时持续不断地触发请求,最终你的客户端网络队列会塞满Ajax调用。

例子:下面代码并不是上一次fn执行完了之后再过100ms才开始执行下一次fn。 事实上,setInterval并不管上一次fn的执行结果,而是每隔100ms就将fn放入异步队列,而两次fn之间具体间隔多久就不一定了,跟setTimeout实际延迟时间类似,和JS执行情况有关。

(function testSetInterval() {
    let i = 0;
    const start = Date.now();
    const timer = setInterval(() => {
        i++;
        i === 2 && clearInterval(timer);
        console.log(`第${i}次开始`, Date.now() - start);
        for(let i = 0; i < 900000000; i++) {}
        console.log(`第${i}次结束`, Date.now() - start);
    }, 100);
})();

VM232:7 第1次开始 104
VM232:9 第1次结束 603
VM232:7 第2次开始 605
VM232:9 第2次结束 1106

虽然每次fn执行时间都很长,但下一次并不是等上一次执行完了再过100ms才开始执行的,实际上早就已经等在队列里了。
在fn被阻塞的时候,setInterval仍然在组织将来对回调函数的调用。 因此,当第一次fn函数调用结束时,已经有6次函数调用在等待执行。

处理可能的阻塞调用

最简单也是最容易控制的方案,是在回调函数内部使用setTimeout函数。

function foo(){
    setTimeout(foo, 100);
}
foo();
参考链接
上一篇 下一篇

猜你喜欢

热点阅读