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

相关推荐
一路向前的月光37 分钟前
react(9)-redux
前端·javascript·react.js
我是哈哈hh2 小时前
【JavaScript进阶】作用域&解构&箭头函数
开发语言·前端·javascript·html
酷酷的阿云2 小时前
Vue3性能优化必杀技:useDebounce+useThrottle+useLazyLoad深度剖析
前端·javascript·vue.js
bin91532 小时前
DeepSeek 助力 Vue 开发:打造丝滑的缩略图列表(Thumbnail List)
前端·javascript·vue.js·ecmascript·deepseek
圣心3 小时前
Ollama 快速入门
开发语言·javascript·人工智能
禾苗种树3 小时前
使用echart的dataZoom的labelFormatter自定义时间范围
前端·javascript·vue.js·echart
m0_748240913 小时前
SpringMVC 请求参数接收
前端·javascript·算法
Hermione_log3 小时前
【vue项目如何利用event-stream实现文字流式输出效果】
前端·javascript·vue.js
Little_Code4 小时前
关于uniapp使用renderJS中调用父类方法和参数的使用
前端·javascript·uni-app
qq_316837754 小时前
uniapp h5端和app端 使用 turn.js
开发语言·javascript·uni-app