setTimeout 引申

简介: setTimeout 引申

正文


一、前言


在 JavaScript 中 setTimeoutsetInterval 最常见不过了,用于延迟或者延迟重复处理等。


setTimeout(() => {
  console.log('一秒后执行')
}, 1000)


这个不能再简单的例子,我们可以简单地理解成:一秒后输出对应的字符串。但是这一秒(delay)只是我们所设的预期值,然而实际情况它只是最小延迟时间而已。也就是说最理想情况下,1 秒之后会执行该匿名函数。


Q:那能否在我们所指定的时间执行对应函数呢?


A:严格来说肯定是不行的,有实实在在的误差在里面,平常看起来像是指定时间执行只是因为我们本身无法感知其中的误差而已。倘若误差在可接受范围内,理解成指定时间后执行也是没问题的。

影响它的因素有很多:比如 for 循环、其他异步任务(微任务、宏任务)、浏览器精度等。本质上是因为 JavaScript 的事件循环机制导致的


// 可自行验证一下,能否在一秒后执行?
setTimeout(() => {
  console.log('会在一秒后执行吗?')
}, 1000)
for(let i = 0; i < 10000; i++) {
  console.count('循环次数')
}


我对 setTimeout 的理解是:在指定时间后,将对应函数作为异步任务加入到任务队列中。

对于刚接触 JavaScript 的朋友,可能会错误地理解为:在指定时间后,执行对应函数。这是不对的,上面的例子就能说明问题。

为什么呢?因为可能一秒的时间内 for 循环还没执行完,所以一秒后还没开始执行定时器里面的函数。我们都知道 setTimeout 属于异步任务(宏任务),在执行(下)一个异步任务之前,首先得执行当前完同步任务、微任务(也属于异步任务)、接着更新UI 之后,才会执行(下一个)异步任务。


二、正题


扯得有点多了,今天的话题是:setTimeout 加不加括号,会导致什么不同的结果?

看以下示例:


function foo() {
  console.log('show foo')
}
// 写法一
setTimeout(foo, 3000)
// 写法二
setTimeout(foo(), 3000)
// 两者运行结果一致吗?


  1. delay 设为 300,看起来好像没区别,都能正常输出 show foo,接着往下看。
  2. 若将 delay 设为 3000,仍然都能输出字符串,但有点区别。setTimeout(foo, 3000)在预期的 3s 后输出值。然而setTimeout(foo(), 3000)好像立刻执行了,而不是等 3s 后才输出。
  3. 通过设置不同 delay 值可以更明显地感知其中的区别,越大越明显。


两者区别:

  1. 不加括号:能正常地按照我们所预期的时候执行对应的函数。
  2. 加括号:同样会执行该函数,但它是立即执行,所以不会达到延迟执行的目的。(这点说法不严谨,只是帮助理解,请继续往下看)


造成上面差异的原因是什么呢?


我们改下代码,就很清晰了。


function foo() {
  console.log('show foo')
  return 'console.log("哈哈")'
}
setTimeout(foo(), 3000)
// 结果:立即打印出 show foo,三秒后打印了 “哈哈”。


因为 foo 函数返回值是 console.log("哈哈"),所以 setTimeout(foo(), 3000) 相当于 setTimeout('console.log("哈哈")', 3000) ,所以出现了上面的结果。其实 setTimeout 方法第一个参数除了支持函数之外,还可以是字符串。若是字符串,会使用  eval 去执行。


由于我们最常用的写法是执行一个匿名函数(如setTimeout(() => {}, delay)),没注意的同学,所以可能会忽略加与不加括号的区别。

还有,极不建议使用 setTimeout('String Code', delay) 的形式。因为 eval 通常被用来执行动态创建的代码,如果 eval(...) 中执行的代码包括一个或多个声明(无论变量还是函数),就会对 eval(...) 所处的词法作用域进行修改。避免出现一些意料之外的事情,不建议使用。


三、扩展


1. 当使用 setTimeout() 方法的时候,是否必须执行 clearTimeout() ?
  • setTimeout() 内的函数执行之前,如果想要阻止执行该方法,只能通过 cleartTimeout() 来处理。
  • setTimeout() 内的函数执行之后,执行 clearTimeout() 方法对整个代码流程没有害处,但是是没有必要的。
  • 通常情况,执行 clearInterval() 比执行 clearTimeout() 更实际一些,因为如果不执行 clearInterval(),则 setInterval() 的方法会无限循环执行下去。而 setTimeout() 在一次调用后,就会停止执行(浏览器会自动回收资源)。除非你创建了一个无限循环的 setTimeout()
2. 关于 setTimeout(fn, 0) 的问题

注意,这仍然属于异步任务,指定某个任务在主线程最早可得的空闲时间执行。HTML5 标准中规定了 setTimeout() 的第二个参数的最小值(最短间隔),不得低于 4 毫秒,如果低于这个值,就会自动增加。在此之前,老版本的浏览器都将最短间隔设为 10 毫秒。另外,对于那些 DOM 的变动(尤其是涉及页面重新渲染部分),通常不会立即执行,而是每 16 毫秒执行一次。这时使用 requestAnimationFrame() 的效果要好于 setTimeout() 。


可以看下文章:

目录
相关文章
|
4天前
|
前端开发
防抖和节流的区别,实现和用处。
防抖和节流是优化高频事件处理的两种技术。防抖确保在一系列连续事件后仅执行最后一次操作,如搜索输入完成后再发送请求;节流则保证在设定时间内仅执行一次操作,适用于滚动加载等场景。两者通过限制回调函数的执行频率,有效提升前端性能。示例代码展示了如何实现这两种技术。
|
1月前
|
Web App开发 存储 前端开发
前端开发必备:requestAnimationFrame、setInterval、setTimeout——功能解析与优劣对比
前端开发必备:requestAnimationFrame、setInterval、setTimeout——功能解析与优劣对比
142 0
|
4月前
|
JavaScript
JS【详解】setTimeout 延时(含清除 setTimeout,计时开始时间,0 秒延时解析,多 setTimeout 执行顺序,setTimeout 应用场景,网红面试题)
JS【详解】setTimeout 延时(含清除 setTimeout,计时开始时间,0 秒延时解析,多 setTimeout 执行顺序,setTimeout 应用场景,网红面试题)
888 0
|
6月前
|
JavaScript 前端开发 UED
JavaScript 持续变化:setTimeout 与 setInterval 的巧妙应用
JavaScript 持续变化:setTimeout 与 setInterval 的巧妙应用
58 1
|
前端开发
一篇搞懂promise
一篇搞懂promise
105 0
|
前端开发 JavaScript
搞懂Promise
搞懂Promise
51 0
|
前端开发
浅尝一颗语法糖 async / await
这是 ES7 提供的语法糖,是真的很友好!
71 0
|
前端开发 JavaScript API
重学前端 17 # Promise里的代码为什么比setTimeout先执行?
重学前端 17 # Promise里的代码为什么比setTimeout先执行?
115 0
重学前端 17 # Promise里的代码为什么比setTimeout先执行?
|
前端开发
每日一题:什么是防抖和节流?有什么区别?如何实现?
每日一题:什么是防抖和节流?有什么区别?如何实现?
758 0
每日一题:什么是防抖和节流?有什么区别?如何实现?
|
前端开发 编译器
让人头秃的promise-then执行顺序问题
`Promise`对于前端开发来说应该不陌生了,其主要用于在一个异步操作中返回结果值,并且支持链式调用。今天就来讨论一个`Promise`链式调用相关的面试题。
743 0
让人头秃的promise-then执行顺序问题