美文网首页
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