JS之setInterval与setTimeout(超详细)

简介: JS之setInterval与setTimeout(超详细)

20210128171834844.jpg


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


setinterval 方法则是表示间隔一定时间反复执行

settimeout 方法是定时程序,不会反复执行,只执行一次


定时器的返回值


当我们设置定时器时(不管是setTimeout还是setInterval),都会有一个返回值。这个返回值是一个数字,代表当前是在浏览器中设置的第几个定时器(返回的是定时器序号)。


let timer1 = setTimeout(() => {}, 1000)
console.log(timer1) // 1
let timer2 = setInterval(() => {},1000)
console.log(timer2) // 2


根据上面两段代码可以知道


1.setTimeout和setInterval虽然是处理不同功能的定时器,但都是浏览器的定时器,所以返回的序号是依次排列的。


2.setInterval设置完成定时器会有一个返回值,不管执行多少次,这个代表序号的返回值不变。


定时器的清除


定时器即使清除了,其返回值也不会清除,之后设置定时器的返回值也会在其返回值的基础上继续向后排,类似于银行的排队领号,即使1号的业务办理完了,后面的人仍是从2号开始继续领号,而不是从1开始。注意: 定时器需要手动清除,并且clearTimeout和clearInterval都可以清除setTimeout或setInterval,但并不建议这样做,容易造成混淆。


clearTimeout([定时器的排队序号])


clearInterval([定时器的排队序号])


clearTimeout(timer1)  // 取消调度
clearInterval(timer2)


定时器的this指向


作为第一个参数的函数将会在全局作用域中执行,因此函数内的this将会指向这个全局对象.


let obj = {
  fn() {
    console.log(this) // obj
    // 示例1
    let timer1 = setTimeout(function() {
      console.log('我是timer1的this指向:', this)  // Window
    }, 1000)
    // 示例2 (让定时器函数中的this是obj:使用变量保存的方式)
    let _this = this
    let timer2 = setTimeout(function() {
      console.log('我是timer2的this指向:', _this) // obj
    }, 1000)
    // 示例3 (让定时器函数中的this是obj:使用bind方法改变this指针)
    let timer3 = setTimeout(
      function() {
        console.log('我是timer3的this指向:', this) // obj
      }.bind(this),
      1000
    )
    // 示例4 (让定时器函数中的this是obj:使用箭头函数,箭头函数中的this继承宿主环境(上级作用域中的this))
    let timer4 = setTimeout(() => {
      console.log('我是timer4的this指向:', this) // obj
    }, 1000)
  }
}
obj.fn()


setTimeout和setInterval是如何工作的?

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


首先,Javascript是一门单线程的非阻塞的脚本语言:用来与浏览器交互。


单线程: 同一时间只能执行一个任务,其他任务就得排队,后续任务必须等到前一个任务结束才能开始执行。


非阻塞: 同步任务直接在主线程队列中顺序执行,而异步任务会进入另一个任务队列,不会阻塞主线程。等到主线程队列空了(执行完了)的时候,就会去异步队列查询是否有可执行的异步任务了(异步任务通常进入异步队列之后还要等一些条件才能执行,如ajax请求、文件读写),如果某个异步任务可以执行了便加入主线程队列,以此循环。


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


微任务: new promise(),new MutaionObserver()


宏任务: setInterval(),setTimeout()

主线程空闲的时候会先去查看微任务队列是否有事件存在,如果存在就会对微任务队列的事件依次调用,直到为空。然后再对宏任务队列依次执行,进入循环。


20210128173625776.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();
目录
相关文章
|
7月前
|
JavaScript 前端开发 开发者
JavaScript中setInterval与setTimeout的异同及使用
【4月更文挑战第22天】JavaScript的`setInterval`和`setTimeout`都用于定时执行任务,但有区别。`setInterval`会按指定间隔反复执行,直到被`clearInterval`停止,可能导致函数堆积;`setTimeout`只执行一次,延迟后执行,适合递归调用来模拟间隔。选择使用时要考虑任务的重复性、执行依赖及可能的性能影响。
|
6月前
|
JavaScript 前端开发 安全
JavaScript基础-定时器:setTimeout, setInterval
【6月更文挑战第13天】JavaScript中的`setTimeout`和`setInterval`是异步编程的关键工具,用于按计划执行代码。`setTimeout`在延迟后执行一次,而`setInterval`则周期性执行。常见问题包括忘记清除定时器导致内存泄漏,递归使用`setTimeout`可能引发无限递归,以及字符串代码执行的安全隐患。解决方法包括使用`clearTimeout`和`clearInterval`,设置递归终止条件,以及优先使用函数表达式。理解定时器的非精确性并采用错误处理策略也是实践中的重要技巧。通过示例展示了如何延迟显示消息和周期性打印计数。
101 2
|
5月前
|
JavaScript 前端开发 数据可视化
js 实现动画的两种方案对比:setTimeout vs RAF (requestAnimationFrame)
js 实现动画的两种方案对比:setTimeout vs RAF (requestAnimationFrame)
77 2
|
5月前
|
JavaScript 前端开发
vue 模拟随机变速的动态打字特效【支持多行文本】(含css实现闪烁光标,js动态改变setInterval定时器的时间间隔)
vue 模拟随机变速的动态打字特效【支持多行文本】(含css实现闪烁光标,js动态改变setInterval定时器的时间间隔)
94 1
|
5月前
|
JavaScript
JS【详解】setTimeout 延时(含清除 setTimeout,计时开始时间,0 秒延时解析,多 setTimeout 执行顺序,setTimeout 应用场景,网红面试题)
JS【详解】setTimeout 延时(含清除 setTimeout,计时开始时间,0 秒延时解析,多 setTimeout 执行顺序,setTimeout 应用场景,网红面试题)
1068 0
|
7月前
|
JavaScript 前端开发 UED
JavaScript 持续变化:setTimeout 与 setInterval 的巧妙应用
JavaScript 持续变化:setTimeout 与 setInterval 的巧妙应用
63 1
|
7月前
|
JavaScript 前端开发 数据处理
JavaScript 游戏规则:setTimeout和setInterval的对决
JavaScript 游戏规则:setTimeout和setInterval的对决
50 1
|
7月前
|
JavaScript 前端开发
为 setTimeout 或 setInterval 提供一个字符串作为第一个参数(js的问题)
为 setTimeout 或 setInterval 提供一个字符串作为第一个参数(js的问题)
40 0
|
7月前
|
JavaScript 前端开发
JS实现可以控制的定时器,setInterval,clearInterval
JS实现可以控制的定时器,setInterval,clearInterval
50 0
|
JavaScript 前端开发 小程序
javascript setinterval 正确的语法
前几天我用setinterval 写了一个小程序,这个setinterval是用来干什么的我就不解释了。 写的方法在其它的浏览器里都能用,后来测试组的同事拿去一测就出了问题。
825 0