重学 setTimeout

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 定时器能准确输出结果。

相关推荐
超人不会飛29 分钟前
就着HTTP聊聊SSE的前世今生
前端·javascript·http
蓝胖子的多啦A梦32 分钟前
Vue+element 日期时间组件选择器精确到分钟,禁止选秒的配置
前端·javascript·vue.js·elementui·时间选选择器·样式修改
夏天想35 分钟前
vue2+elementui使用compressorjs压缩上传的图片
前端·javascript·elementui
The_cute_cat36 分钟前
JavaScript的初步学习
开发语言·javascript·学习
海天胜景39 分钟前
vue3 el-table 列增加 自定义排序逻辑
javascript·vue.js·elementui
烛阴1 小时前
XPath 进阶:掌握高级选择器与路径表达式
前端·javascript
独立开阀者_FwtCoder1 小时前
URL地址末尾加不加 "/" 有什么区别
前端·javascript·github
独立开阀者_FwtCoder1 小时前
Vue3 新特性:原来watch 也能“暂停”和“恢复”了!
前端·javascript·github
前端小巷子2 小时前
跨域问题解决方案:开发代理
前端·javascript·面试
JohnYan2 小时前
Bun技术评估 - 07 S3
javascript·后端·bun