搜索
首页web前端js教程面试:JavaScript中的setTimeout到底是什么?

本篇文章给大家带来的内容是关于面试:JavaScript中的setTimeout到底是什么?,有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助。

在刷笔试题的时候,经常会碰到setTimeout的问题,只知道这个是设置定时器;但是考察的重点一般是在一个方法中包含了定时器,定时器中的打印和方法中打印的执行顺序问题,也许我说的有点儿难懂,下面就来看看setTimeout到底是什么吧!

定时器的介绍

js中有哪些定时器?

周期定时器:setInterval()

介绍

setInterval()是按照指定的周期来调用定时器,方法会不断的调用定时器,直到使用clearInterval()停止或者窗口关闭。

语法

setInterval(code,millisec,lang)

  • code:要执行的方法体(必选)

  • millisec:每隔多少毫秒执行一次(单位是毫秒,如果设置为5000,即每5秒执行一次)(必选)

  • lang:指使用的语言(可选)

实例

通过setInterval实现时钟效果

<html>
<body>

<input type="text" id="clock" />
<script type="text/javascript">
//每隔1秒执行一次clock方法
var int=self.setInterval("clock()",1000);
function clock()
{
var d=new Date();
var t=d.toLocaleTimeString();
document.getElementById("clock").value=t;
}
</script>
<!-- 设置一个按钮,点击按钮即停止定时器 -->
<button onclick="int=window.clearInterval(int)">停止</button>
</body>
</html>

效果图:

3657187851-5becc39bb7c8b_articlex.gif

‘一次性’定时器:setTimeout()

介绍

  顾名思义,这个定时器只会执行一次,和setInterval()的区别就在这儿了,正是因为如此,setInterval()才需要使用clearInterval方法去取消定时器

语法

  setTimeout(code,millisec,lang)    ps:每个参数的含义和setInterval()的均相同

实例

  点击按钮3秒后弹出“Hello”

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>菜鸟教程(runoob.com)</title>
</head>
<body>

<p>点击按钮,在等待 3 秒后弹出 "Hello"。</p>
<button onclick="myFunction()">点我</button>

<script>
function myFunction()
{
    setTimeout(function(){alert("Hello")},3000);
}
</script>

</body>
</html>

效果图:

1860076736-5bed7234abbf1_articlex.gif

取消定时器

介绍

  使用计时器ID来取消计时器回调的发生,每个计时器都会返回一个id,是为了取消定时器的方法可以获取到相应的计数器。

  • clearInterval(id)

  • clearTimeout(id)

实例
//设置超时调用
var timeoutId = setTimeout(function (){
    alert("hello World");
    },1000);
//取消掉用的代码
clearTimeout(timeoutId);

setTimeout的执行顺序到底是怎样的?

  我们都知道,js是单线程语言,所有的多线程都是假象,都是单线程模拟出来的。浏览器是多进程的,而浏览器的内核(渲染进程)是多线程的。
  渲染进程中有一个js引擎线程,这个线程是用来处理javaScript脚本的(例如chrome的V8引擎),而我们一直说的javaScript是单线程的就是因为这个。
  那么问题来了,既然js是单线程的,那setTimeout的异步是怎么实现的呢?js在解析脚本的时候,会将任务分为两大类,同步任务和异步任务,它们在解析时会进入不同的场所执行。

  • 同步任务:会进入主线程的执行栈,也就是js引擎线程管理的地方,按照顺序执行

  • 异步任务:进入Event Table中,并注册函数,当回调函数的条件满足时,就会将回调函数放进Event Queue中,也就是任务队列中。

  当主线程中的任务执行完毕后,也就是执行栈为空时,就会去任务队列中看有没有事件,如果有的话,就进入主线程执行,一直这样循环下去,这就是事件循环机制了,可以参照下面的图理解一下:

2823950046-5bf0e5f46ade7_articlex.png

  也许你对事件循环机制的过程还是不太明白,那么我再解释清楚一点。例如下面这个例子:

   console.log('start')
   setTimeout(function(){
     console.log('setTimeout')
   },5000)
   console.log('end')

执行过程:

  • 开始解析,遇到console.log,是同步任务,进入主线程,直接执行,打印start

  • 往下走,遇到setTimeout,是异步任务,进入Event Table,并注册回调函数;

  • 再往下走,遇到console.log,直接执行,打印end

  • 5s后,将回调函数放进Event Queue,此时执行栈刚好为空,主线程会去任务队列中取出这个回调函数,执行,打印setTimeout

  ps:

  • 第1,3步都是js引擎线程干的事情,主线程执行任务;

  • 第2步是渲染进程中的事件触发线程(专门管理任务队列的)管理;

  • 第4步是定时器线程控制的(也就是setTiemout和setInterval所在的进程),定时器线程专门用来控制什么时候将回调函数放进任务队列。

  如果看懂了上面的例子,就知道其实setTimeout的第二个参数其实并不能准确的控制多少秒后执行里面的函数,而是控制多少秒后将这个函数放进任务队列中;这样也就同样可以解释,为什么有时候明明设置的是2秒之后执行,却要等不止2秒(因为很有可能定时线程将回调函数放进任务队列后,主线程还在执行执行栈中的任务,需要执行栈中的任务全部执行完后才会去任务队列中取任务)。
  这样就会引发一个问题,我们知道setInterval是隔一定的时间执行一次,现在理解了原理后,就知道其实是隔一定的时间定时器线程将回调函数放进任务队列中。如果已经将回调函数放进任务队列,但是主线程正在执行一个非常耗时的任务,当这个任务执行完毕后,主线程去任务队列中取任务,这个时候,就会出现连续执行的情况,也就是说setInterval相当于失效了。

setTimeout基础篇

  这一部分主要是针对在事件循环机制中setTimeout调顺序进行举例子,如果能够轻松的将例子看懂,就说明你是真的懂了事件循环机制的一部分,为什么说是一部分呢,因为还有一个宏任务和微任务的知识点还没有涉及到,后面的进阶篇就会涉及到啦!

例1

  console.log('start')
  setTimeout(function(){
    console.log('setTimeout')
  },0)
  console.log('end')

打印结果:(如果前面看懂了的同学应该就会明白为什么)

469556685-5bf134fe68f1a_articlex.png

分析:其实和上面那个例子时一样的,只是这个0会给我们一种会立即执行的假象,这个0是说明定时器线程会立即将回调函数放进任务队列而已,主线程还是会将执行栈中的两个同步任务执行完成后再去任务队列中取任务,所以执行顺序和这里的秒数无关。而且即使执行栈为空,也不会0秒就执行,因为HTML的标准规定,setTimeout不超过4ms按照4ms来计算。

例2

  console.log('start')
  setTimeout(function(){
    console.log('setTimeout')
  }(),0)
  console.log('end')

打印结果:(仔细对比与例1的区别)

3418896850-5bf136c89d03f_articlex.png

分析:细心的同学会发现,我将回调函数改成了立即执行函数,就改变了执行的顺序。首先我们需要明确的是setTimeout的第一个参数是指函数的返回值,这里回调函数为立即执行函数时,返回值就是undefined了,所以会直接执行立即执行函数,也就是立即打印setTimeout,而真正的setTimeout函数就相当于没起作用。

例3

setTimeout(() => {
   console.log('setTimeout')
},3000)

sleep(10000000)//伪代码,表示这个函数要执行很久很久

打印结果:
这个结果不说也知道,肯定会打印出setTimeout的,但是重点却不在这儿~
重点在于,这个setTimeout是隔很久很久打印出来的,远远超过了3秒,这个例子也是很明确的体现了js的事件循环机制。

setTimeout进阶篇

  这一部分相对于基础篇,加上了作用域以及其他也是比较难以理解的东西,可能还需要补充一些其他知识才会明白,我会尽量讲清楚,也会把我看的参考文章放在下面。
  受到一篇文章的启发,我们以循序渐进的方式来阐述

难度:O

问题:以下代码输出的是什么?

for(var i = 0;i < 5;i++){
    console.log(i)
  }

答案:没错,你没有看错,就是一个简单的循环,就像你想的那样,连续输出0,1,2,3,4

难度:OO

问题:以下代码输出的是什么?如果把时间改为1000*i输出的又是什么?

for(var i = 0;i < 5;i++){
    setTimeout(function(){
      console.log(i)
    },1000)
  }

答案:
  时间为1000时,1秒后会连续输出5个5;时间为1000*i时,会每隔一秒输出一个5,一共5个5
分析:
  由上面的事件循环机制我们知道,setTimeout是异步事件,会放在事件队列中等着主线程来执行,这个时候for循环中的i已经变成了5,由于定时器线程是在1秒后直接将5个setTimeout事件放进事件队列中,所以主线程在执行的时候就没有间隔了;当时间乘上一个i时,定时器会隔1秒将setTimeout事件放入队列,就会出现每隔一秒输出一个5的情况。

难度:OOO

问题:如果想输出0,1,2,3,4应该怎么改?
分析:
  出现上一题的情况主要是因为在setTimeout的回调函数中并没有保存每次循环i的值,最后执行的时候,得到的i就是最后更新的i了(即为5),所以要解决这个问题,思路是要在回调函数中保存每次for循环中的i值。

解决方案1:使用es6中let代替var
分析:let是es6中新增的内容,作用和var一样,都是用来定义变量,但是最大的差别就是let会形成块级作用域,在本例中,就是每次循环,都会产生一个作用域,在该作用域中的变量是一个固定值,下次i变化时不会对这个i产生影响,也就是达到了我们的目标。

for(let i = 0;i < 5;i++){
    setTimeout(function(){
      console.log(i)
    },1000*i)
  }

解决方案2:使用闭包
分析:就是直接在setTimeout函数的外面套一层立即执行函数,并将i值作为参数传到匿名函数中(这里的匿名函数也可以是命名函数),然后由于setTimeout中回调函数用到了匿名函数中的i,就会形成闭包。

for(var i = 0;i < 5;i++){
    (function(i){
      setTimeout(function(){
        console.log(i)
      }, 1000 * i)
    }) (i) 
  }

延伸:将代码变成下面这样会输出什么?(去掉匿名函数中的i)
分析:这里会输出5个5,也就是闭包没有起作用,根本原因是i并没有传进去,打印的还是最后的i

for (var i = 0; i < 5; i++) {
      (function () {
        setTimeout(function () {
          console.log(i)
        }, i * 1000)
      })(i);
    }

解决方案3:将回调函数改成立即执行函数
分析:这个解决方案其实不是太好,如果要求是每隔1秒输出一个数字,这个方法就不适用了;这个方法会立马输出0,1,2,3,4,原因结合基础篇应该就明白了

for (var i = 0; i < 5; i++) {
        setTimeout((function (i){
          console.log(i);
        })(i), i * 1000)
    }

难度:OOOO

  这一部分会涉及到promise,事件循环机制,宏任务和微任务的内容,算是比较难的部分了,如果觉得比较难看懂,最好先去补一下基础知识,我这里就简单介绍一下。

promise对象

我这里就不详细讲了。

宏任务和微任务

  • 宏任务:可以理解成将代码块走一遍的过程,setTimeout和promise都是宏任务,现在不理解没关系,后面会通过例子帮助理解

  • 微任务:是在宏任务执行完成之后执行的,也是有相应的微任务队列存放微任务,比如promise中的then就是微任务

3689241242-5bfa8bca8ce9a_articlex.png

问题:以下代码输出的是什么?

    setTimeout(function () {
      console.log(1)
    }, 0);
    new Promise(function executor(resolve) {
      console.log(2);
      for (var i = 0; i < 10000; i++) {
        i == 9999 && resolve();
      }
      console.log(3);
    }).then(function () {
      console.log(4);
    });
    console.log(5);

答案:(是不是很懵,为什么会是这样,下面看我的分析你就知道了)

3773370169-5bfa8e7596e0a_articlex.png

分析:

  1. 进入宏任务(从第一行到最后一行执行一遍的过程),碰到setTimeout,将setTimeout放进事件队列中;

  2. 碰到promise,执行console,打印2

  3. 经过循环后,执行console,打印3

  4. 到了then,由于then是微任务,会在宏任务执行完成后执行,放进微任务队列;

  5. 遇到console,打印5

  6. 至此,第一次的宏任务执行完成,接下来执行微任务队列中的then,打印4

  7. 现在执行栈中的任务都执行完了,现在就要去事件队列中取事件,此时执行setTimeout这个宏任务,打印1

宏任务微任务与同步事件异步事件的关系:
  这些词都是用来描述事件的,只是从不同的角度来描述,就像是胖子矮子与男生女生之间的联系


以上是面试:JavaScript中的setTimeout到底是什么?的详细内容。更多信息请关注PHP中文网其他相关文章!

声明
本文转载于:segmentfault。如有侵权,请联系admin@php.cn删除
JavaScript是用C编写的吗?检查证据JavaScript是用C编写的吗?检查证据Apr 25, 2025 am 12:15 AM

是的,JavaScript的引擎核心是用C语言编写的。1)C语言提供了高效性能和底层控制,适合JavaScript引擎的开发。2)以V8引擎为例,其核心用C 编写,结合了C的效率和面向对象特性。3)JavaScript引擎的工作原理包括解析、编译和执行,C语言在这些过程中发挥关键作用。

JavaScript的角色:使网络交互和动态JavaScript的角色:使网络交互和动态Apr 24, 2025 am 12:12 AM

JavaScript是现代网站的核心,因为它增强了网页的交互性和动态性。1)它允许在不刷新页面的情况下改变内容,2)通过DOMAPI操作网页,3)支持复杂的交互效果如动画和拖放,4)优化性能和最佳实践提高用户体验。

C和JavaScript:连接解释C和JavaScript:连接解释Apr 23, 2025 am 12:07 AM

C 和JavaScript通过WebAssembly实现互操作性。1)C 代码编译成WebAssembly模块,引入到JavaScript环境中,增强计算能力。2)在游戏开发中,C 处理物理引擎和图形渲染,JavaScript负责游戏逻辑和用户界面。

从网站到应用程序:JavaScript的不同应用从网站到应用程序:JavaScript的不同应用Apr 22, 2025 am 12:02 AM

JavaScript在网站、移动应用、桌面应用和服务器端编程中均有广泛应用。1)在网站开发中,JavaScript与HTML、CSS一起操作DOM,实现动态效果,并支持如jQuery、React等框架。2)通过ReactNative和Ionic,JavaScript用于开发跨平台移动应用。3)Electron框架使JavaScript能构建桌面应用。4)Node.js让JavaScript在服务器端运行,支持高并发请求。

Python vs. JavaScript:比较用例和应用程序Python vs. JavaScript:比较用例和应用程序Apr 21, 2025 am 12:01 AM

Python更适合数据科学和自动化,JavaScript更适合前端和全栈开发。1.Python在数据科学和机器学习中表现出色,使用NumPy、Pandas等库进行数据处理和建模。2.Python在自动化和脚本编写方面简洁高效。3.JavaScript在前端开发中不可或缺,用于构建动态网页和单页面应用。4.JavaScript通过Node.js在后端开发中发挥作用,支持全栈开发。

C/C在JavaScript口译员和编译器中的作用C/C在JavaScript口译员和编译器中的作用Apr 20, 2025 am 12:01 AM

C和C 在JavaScript引擎中扮演了至关重要的角色,主要用于实现解释器和JIT编译器。 1)C 用于解析JavaScript源码并生成抽象语法树。 2)C 负责生成和执行字节码。 3)C 实现JIT编译器,在运行时优化和编译热点代码,显着提高JavaScript的执行效率。

JavaScript在行动中:现实世界中的示例和项目JavaScript在行动中:现实世界中的示例和项目Apr 19, 2025 am 12:13 AM

JavaScript在现实世界中的应用包括前端和后端开发。1)通过构建TODO列表应用展示前端应用,涉及DOM操作和事件处理。2)通过Node.js和Express构建RESTfulAPI展示后端应用。

JavaScript和Web:核心功能和用例JavaScript和Web:核心功能和用例Apr 18, 2025 am 12:19 AM

JavaScript在Web开发中的主要用途包括客户端交互、表单验证和异步通信。1)通过DOM操作实现动态内容更新和用户交互;2)在用户提交数据前进行客户端验证,提高用户体验;3)通过AJAX技术实现与服务器的无刷新通信。

See all articles

热AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智能驱动的应用程序,用于创建逼真的裸体照片

AI Clothes Remover

AI Clothes Remover

用于从照片中去除衣服的在线人工智能工具。

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

Video Face Swap

Video Face Swap

使用我们完全免费的人工智能换脸工具轻松在任何视频中换脸!

热工具

Dreamweaver Mac版

Dreamweaver Mac版

视觉化网页开发工具

VSCode Windows 64位 下载

VSCode Windows 64位 下载

微软推出的免费、功能强大的一款IDE编辑器

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

安全考试浏览器

安全考试浏览器

Safe Exam Browser是一个安全的浏览器环境,用于安全地进行在线考试。该软件将任何计算机变成一个安全的工作站。它控制对任何实用工具的访问,并防止学生使用未经授权的资源。

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具