美文网首页
setTimeout() 和 setInterval() 的运行

setTimeout() 和 setInterval() 的运行

作者: tanyp | 来源:发表于2017-04-08 18:20 被阅读0次

    setTimeoutsetInterval的运行机制是,将指定的代码移出本次执行,等到下一轮Event Loop时,再检查是否到了指定时间。如果到了,就执行对应的代码;如果不到,就等到再下一轮Event Loop时重新判断。

    这意味着,setTimeoutsetInterval指定的代码,必须等到本轮Event Loop的所有同步任务都执行完,再等到本轮Event Loop的“任务队列”的所有任务执行完,才会开始执行。由于前面的任务到底需要多少时间执行完,是不确定的,所以没有办法保证,setTimeoutsetInterval指定的任务,一定会按照预定时间执行。

    一、setTimeout(f,0)

    setTimeout(function() {
      console.log("Timeout");
    }, 0);
    
    function a(x) {
      console.log("a() 开始运行");
      b(x);
      console.log("a() 结束运行");
    }
    
    function b(y) {
      console.log("b() 开始运行");
      console.log("传入的值为" + y);
      console.log("b() 结束运行");
    }
    

    运行结果是

    // 当前任务开始
    // a() 开始运行
    // b() 开始运行
    // 传入的值为42
    // b() 结束运行
    // a() 结束运行
    // 当前任务结束
    // Timeout
    

    上面代码说明,setTimeout(f, 0) 必须要等到当前脚本的所有同步任务结束后才会执行。
    即使消息队列是空的,0毫秒实际上也是达不到的。根据HTML 5标准,setTimeout
    推迟执行的时间,最少是4毫秒。如果小于这个值,会被自动增加到4。这是为了防止多个setTimeout(f, 0)语句连续执行,造成性能问题。

    另一方面,浏览器内部使用32位带符号的整数,来储存推迟执行的时间。这意味着setTimeout 最多只能推迟执行2147483647毫秒(24.8天),超过这个时间会发生溢出,导致回调函数将在当前任务队列结束后立即执行,即等同于setTimeout(f, 0)的效果。

    setTimeout(f, 0)有几个非常重要的用途。它的一大应用是,可以调整事件的发生顺序。由于setTimeout(f,0)实际上意味着,将任务放到浏览器最早可得的空闲时段执行,所以那些计算量大、耗时长的任务,常常会被放到几个小部分,分别放到setTimeout(f,0)里面执行。

    二、正常任务与微任务

    正常情况下,JavaScript的任务是同步执行的,即执行完前一个任务,然后执行后一个任务。只有遇到异步任务的情况下,执行顺序才会改变。

    这时,需要区分两种任务:正常任务(task)与微任务(microtask)。它们的区别在于,“正常任务”在下一轮Event Loop执行,“微任务”在本轮Event Loop的所有任务结束后执行。

    console.log(1);
    
    setTimeout(function() {
      console.log(2);
    }, 0);
    
    Promise.resolve().then(function() {
      console.log(3);
    }).then(function() {
      console.log(4);
    });
    
    console.log(5);
    

    运行结果

    // 1
    // 5
    // 3
    // 4
    // 2
    

    上面代码的执行结果说明,setTimeout(fn, 0)在Promise.resolve之后执行。

    这是因为setTimeout语句指定的是“正常任务”,即不会在当前的Event Loop执行。而Promise会将它的回调函数,在状态改变后的那一轮Event Loop指定为微任务。所以,3和4输出在5之后、2之前。

    正常任务包括以下情况。

    setTimeout
    setInterval
    setImmediate
    I/O
    各种事件(比如鼠标单击事件)的回调函数

    微任务目前主要是process.nextTick和 Promise 这两种情况。

    本文来自阮一峰博客

    相关文章

      网友评论

          本文标题:setTimeout() 和 setInterval() 的运行

          本文链接:https://www.haomeiwen.com/subject/swtrattx.html