html.spec.whatwg.org/multipage/t...
这是最新关于 setTimeout
的标准。
1. 简单使用
js
setTimeout(() => {
console.log("回调了")
}, 0)
在 0ms
后执行回调。其中第二个参数的单位是 ms
,所以要想在 1s
内执行回调,那么就传递 1000
即可。
2. 函数定义和说明
我们看看 setTimeout
的函数定义( Web IDL ):
c++
long setTimeout(TimerHandler handler, optional long timeout = 0, any... arguments);
具体解释一下每一个参数的含义:
- handler
TimerHandler
类型,也就是回调函数,或者字符串。
c++
typedef (DOMString or Function) TimerHandler;
当是字符串的时候代表 code
,也就是到时间会执行字符串代码:
js
function test() {
console.log("Hello wujingyue.")
}
setTimeout("test()", 10)
// 会打印: Hello wujingyue.
- timeout 超时的时间,默认为
0
,是一个可选的参数。 - arguments 可选的参数,用于传递给 handler 参数的,在有一些情况下,比如:
js
function test(name) {
console.log(`我的名字是:${name}`)
}
setTimeout(test, 0, "吴敬悦")
其中"吴敬悦"就会传递给 test
函数,这样我们不用再写回调了。
3. 消除之前的理解
消除之前的理解,我们可以在 6.3 Timers --- HTML5 上看到下面这句话:
If the currently running task is a task that was created by the setTimeout() method, and timeout is less than 4, then increase timeout to 4.
如果当前正在运行的任务是通过setTimeout()方法创建的任务,并且超时时间小于4,则将超时时间增加到4。
也就是当我们的参数 timeout
小于 4
的时候,就会将 timeout
设置成 4
。现在的标准中已经不是这样的了,而且目前我的 chrome v122.0.6261.70
版本也已经改成最新标准的实现了,而不是上面描述的这种实现。下面我们验证一下:
js
const startTime = Date.now()
function test(name) {
console.log(Date.now() - startTime) // 3
}
setTimeout(test, 0)
首先我们知道这里的这个时间不一定准确,至于原因在标准中也说明了。
This API does not guarantee that timers will run exactly on schedule. Delays due to CPU load, other tasks, etc, are to be expected.
该API不能保证定时器会准确按计划运行。由于CPU负载、其他任务等造成的延迟是可以预料的。
当有一点我们是清楚的,也就是上面的打印小于 4
了,说明小于4就设置成4
的结论是错误的。在我的电脑上测试,其实出现小于 4
的情况是不常见的,但只要出现过就足以说明了。
虽然只有一层不会出现小于 4
就自动设置成 4
的情况,但是如果你的嵌套层级大于 5
,那么就会使用上面的规则。
js
setTimeout(() => {
// 第一层嵌套
setTimeout(() => {
// 第二层嵌套
setTimeout(() => {
// 第三层嵌套
setTimeout(() => {
// 第四层嵌套
setTimeout(() => {
// 第五层嵌套
setTimeout(() => {
// 第六层嵌套
// 这里的 timeout 如果设置的是小于 4 的数,那么就会设置成 4
// 我这里写的是 0 ,会被改成 4 ,这里规则是大于 5 层嵌套都会这样
}, 0)
}, 0)
}, 0)
}, 0)
}, 0)
}, 0)
具体可以验证一下,你会发现大于 5
层所执行的时间不会小于 4
。这个规则具体看标准中的第五条。
If nesting level is greater than 5, and timeout is less than 4, then set timeout to 4.
如果嵌套级别大于5,并且超时时间小于4,则将超时时间设置为4
4. 有趣的代码
js
var log = '';
function logger(s) { log += s + ' '; }
// 1 层 setTimeout
setTimeout({ toString: function () {
// 2 层 setTimeout
setTimeout("logger('ONE')", 100);
return "logger('TWO')";
} }, 100);
setTimeout(() => {
console.log(log) // ONE TWO
}, 101)
我们看到这里传入的是对象;像这样的代码 setTimeout
在执行的时候会先对传入的参数进行 toString
,当然前提是传入的类型是数组,对象这样的引用类型。上面代码的执行顺序是:
注意虽然这里写了两个大约 100ms
后,但实际情况是这两个 100ms
是并行的,所以我下面的 101
定时器能准确输出结果。