JS【详解】setTimeout 延时(含清除 setTimeout,计时开始时间,0 秒延时解析,多 setTimeout 执行顺序,setTimeout 应用场景,网红面试题)

avatar
作者
筋斗云
阅读量:0

setTimeout 简介

setTimeout 是一个 js 内置的函数,用于延时执行代码

  • 参数1:回调函数,延迟一段时间后执行的代码
  • 参数2:延迟的时间,单位是毫秒。(默认为 0 毫秒)
  • 返回值:计时器的ID,是一个整数(例子中的 timer)。
const timer = setTimeout(function() {     console.log(1); // 3 秒后,打印 1 }, 3000); 

清除 setTimeout

clearTimeout(timer); // timer 为计时器的ID 

setTimeout 什么时候开始计时?

首先, setTimeout 属于 js 异步任务中的宏任务

在这里插入图片描述
如上图可见,宏任务需等待同步任务、微任务、DOM渲染完成后,通过事件轮询触发执行,所以存在复杂异步逻辑时,很难精准预判 setTimeout 的开始计时时间。

逻辑简单的,比较好分析,如

function test() {   print("开始");    setTimeout(() => {     print("执行 setTimeout");   }, 1000);    print("结束"); } 
  • setTimeout 从打印 “结束” 后开始计时
  • 从计时开始 1 秒后,打印 “ 执行 setTimeout ”

下方代码可以展示得更清晰:

function print(info) {   let dt = new Date();   var y = dt.getFullYear();   var mt = dt.getMonth() + 1;   var day = dt.getDate();   var h = dt.getHours(); //获取时   var m = dt.getMinutes(); //获取分   var s = dt.getSeconds(); //获取秒   let str =     "当前时间:" +     y +     "年" +     mt +     "月" +     day +     "日" +     h +     "时" +     m +     "分" +     s +     "秒";    console.log(info + "————" + str); }  function sleep(delay) {   var start = new Date().getTime();   while (new Date().getTime() - start < delay) {     continue;   }   print("sleep 执行完毕"); }  function test() {   print("开始");    sleep(1000);    setTimeout(() => {     print("执行 setTimeout");   }, 1000);    print("结束"); }  test(); 

执行结果

开始————当前时间:2024529141123秒 sleep 执行完毕————当前时间:2024529141124秒 结束————当前时间:2024529141124秒 执行 setTimeout————当前时间:2024529141125

0 秒延时并不代表立即执行!

print("同步任务执行开始");  setTimeout(function () {   print("setTimeout延时0秒执行"); }, 0);  print("同步任务执行结束"); 

执行结果

同步任务执行开始————当前时间:2024529144229秒 同步任务执行结束————当前时间:2024529144229秒 setTimeout延时0秒执行————当前时间:2024529144229

一旦添加了 setTimeout ,便是一个异步宏任务,需等同步任务、异步微任务、DOM渲染完成后,通过事件轮询触发执行。

多个 setTimeout 的执行顺序 ?

function test() {   console.time("本段代码总耗时");    print("同步任务执行开始");    setTimeout(function () {     print("setTimeout延时3秒执行");     console.timeEnd("本段代码总耗时");   }, 3000);    setTimeout(function () {     print("第1个setTimeout延时2秒执行");   }, 2000);    setTimeout(function () {     print("第2个setTimeout延时2秒执行");   }, 2000);    print("同步任务执行结束"); }  test(); 

执行结果

同步任务执行开始————当前时间:2024529142120秒 同步任务执行结束————当前时间:2024529142120秒 第1个setTimeout延时2秒执行————当前时间:2024529142122秒 第2个setTimeout延时2秒执行————当前时间:2024529142122秒 setTimeout延时3秒执行————当前时间:2024529142123本段代码总耗时: 3.005s 
  • 所有 setTimeout 的开始计时时间几乎相同
  • 延时相同的 setTimeout ,会按 setTimeout 的出现的先后顺序执行
  • 延时不同的 setTimeout ,延时越久的 setTimeout 越晚执行

setTimeout 的应用场景

5秒后关闭网页两侧的广告栏

window.onload = function () {     //获取相关元素     var imgArr = document.getElementsByTagName("img");     //设置定时器:5秒后关闭两侧的广告栏     setTimeout(fn,5000);     function fn(){         imgArr[0].style.display = "none";         imgArr[1].style.display = "none";     } } 

setTimeout 的网红面试题

下方代码的执行结果是?

for (var i = 0; i < 10; i++) {   setTimeout(() => {     console.log(i);   }); } 

答案

10 10 10 10 10 10 10 10 10 10 

解析 :

setTimeout 是异步宏任务,for 循环是同步任务,for 循环先执行,依次在Web APIs 中添加了10个setTimeout,待 for 循环完毕,i 的值已变为 10 ,此时才开始事件轮询,setTimeout 依次开始计时,因延时为0秒,最终效果为 for 循环完毕后,立马一次执行 10 次 setTimeout 的回调,即依次打印 10 个 10

延展提问:怎样改动实现打印 0 到 9 ?

答案1:将 var 改成 let

for (let i = 0; i < 10; i++) {   setTimeout(() => {     console.log(i);   }); } 

解析:let 声明的变量具有局部作用域, var 声明的变量是全局作用域,改用 let 后,执行 setTimeout 内的回调函数时,取到的 i 值为 setTimeout 被放入 Web APIs 时的值,即 for 循环时 i 的值。

答案2:改用立即执行函数包裹

for (var i = 0; i < 10; i++) {   (function (i) {     setTimeout(() => {       console.log(i);     });   })(i); } 

解析:立即执行函数的原理和 let 类似,也是形成了局部作用域,实现了预期效果。

广告一刻

为您即时展示最新活动产品广告消息,让您随时掌握产品活动新动态!