目录
‘一次性’定时器:setTimeout()
介绍" >介绍
语法" >语法
实例" >实例
取消定时器
setTimeout的执行顺序到底是怎样的?
setTimeout基础篇
例3
setTimeout进阶篇
难度:O
难度:OO
难度:OOO
难度:OOOO
promise对象
宏任务和微任务
首页 web前端 js教程 面试:JavaScript中的setTimeout到底是什么?

面试:JavaScript中的setTimeout到底是什么?

Nov 26, 2018 pm 03:33 PM
javascript

本篇文章给大家带来的内容是关于面试: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中文网其他相关文章!

本站声明
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热AI工具

Undresser.AI Undress

Undresser.AI Undress

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

AI Clothes Remover

AI Clothes Remover

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

Undress AI Tool

Undress AI Tool

免费脱衣服图片

Clothoff.io

Clothoff.io

AI脱衣机

AI Hentai Generator

AI Hentai Generator

免费生成ai无尽的。

热门文章

R.E.P.O.能量晶体解释及其做什么(黄色晶体)
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.最佳图形设置
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
R.E.P.O.如果您听不到任何人,如何修复音频
3 周前 By 尊渡假赌尊渡假赌尊渡假赌
WWE 2K25:如何解锁Myrise中的所有内容
3 周前 By 尊渡假赌尊渡假赌尊渡假赌

热工具

记事本++7.3.1

记事本++7.3.1

好用且免费的代码编辑器

SublimeText3汉化版

SublimeText3汉化版

中文版,非常好用

禅工作室 13.0.1

禅工作室 13.0.1

功能强大的PHP集成开发环境

Dreamweaver CS6

Dreamweaver CS6

视觉化网页开发工具

SublimeText3 Mac版

SublimeText3 Mac版

神级代码编辑软件(SublimeText3)

如何使用WebSocket和JavaScript实现在线语音识别系统 如何使用WebSocket和JavaScript实现在线语音识别系统 Dec 17, 2023 pm 02:54 PM

如何使用WebSocket和JavaScript实现在线语音识别系统引言:随着科技的不断发展,语音识别技术已经成为了人工智能领域的重要组成部分。而基于WebSocket和JavaScript实现的在线语音识别系统,具备了低延迟、实时性和跨平台的特点,成为了一种被广泛应用的解决方案。本文将介绍如何使用WebSocket和JavaScript来实现在线语音识别系

WebSocket与JavaScript:实现实时监控系统的关键技术 WebSocket与JavaScript:实现实时监控系统的关键技术 Dec 17, 2023 pm 05:30 PM

WebSocket与JavaScript:实现实时监控系统的关键技术引言:随着互联网技术的快速发展,实时监控系统在各个领域中得到了广泛的应用。而实现实时监控的关键技术之一就是WebSocket与JavaScript的结合使用。本文将介绍WebSocket与JavaScript在实时监控系统中的应用,并给出代码示例,详细解释其实现原理。一、WebSocket技

如何使用WebSocket和JavaScript实现在线预约系统 如何使用WebSocket和JavaScript实现在线预约系统 Dec 17, 2023 am 09:39 AM

如何使用WebSocket和JavaScript实现在线预约系统在当今数字化的时代,越来越多的业务和服务都需要提供在线预约功能。而实现一个高效、实时的在线预约系统是至关重要的。本文将介绍如何使用WebSocket和JavaScript来实现一个在线预约系统,并提供具体的代码示例。一、什么是WebSocketWebSocket是一种在单个TCP连接上进行全双工

如何利用JavaScript和WebSocket实现实时在线点餐系统 如何利用JavaScript和WebSocket实现实时在线点餐系统 Dec 17, 2023 pm 12:09 PM

如何利用JavaScript和WebSocket实现实时在线点餐系统介绍:随着互联网的普及和技术的进步,越来越多的餐厅开始提供在线点餐服务。为了实现实时在线点餐系统,我们可以利用JavaScript和WebSocket技术。WebSocket是一种基于TCP协议的全双工通信协议,可以实现客户端与服务器的实时双向通信。在实时在线点餐系统中,当用户选择菜品并下单

JavaScript和WebSocket:打造高效的实时天气预报系统 JavaScript和WebSocket:打造高效的实时天气预报系统 Dec 17, 2023 pm 05:13 PM

JavaScript和WebSocket:打造高效的实时天气预报系统引言:如今,天气预报的准确性对于日常生活以及决策制定具有重要意义。随着技术的发展,我们可以通过实时获取天气数据来提供更准确可靠的天气预报。在本文中,我们将学习如何使用JavaScript和WebSocket技术,来构建一个高效的实时天气预报系统。本文将通过具体的代码示例来展示实现的过程。We

简易JavaScript教程:获取HTTP状态码的方法 简易JavaScript教程:获取HTTP状态码的方法 Jan 05, 2024 pm 06:08 PM

JavaScript教程:如何获取HTTP状态码,需要具体代码示例前言:在Web开发中,经常会涉及到与服务器进行数据交互的场景。在与服务器进行通信时,我们经常需要获取返回的HTTP状态码来判断操作是否成功,根据不同的状态码来进行相应的处理。本篇文章将教你如何使用JavaScript获取HTTP状态码,并提供一些实用的代码示例。使用XMLHttpRequest

javascript中如何使用insertBefore javascript中如何使用insertBefore Nov 24, 2023 am 11:56 AM

用法:在JavaScript中,insertBefore()方法用于在DOM树中插入一个新的节点。这个方法需要两个参数:要插入的新节点和参考节点(即新节点将要被插入的位置的节点)。

如何在JavaScript中获取HTTP状态码的简单方法 如何在JavaScript中获取HTTP状态码的简单方法 Jan 05, 2024 pm 01:37 PM

JavaScript中的HTTP状态码获取方法简介:在进行前端开发中,我们常常需要处理与后端接口的交互,而HTTP状态码就是其中非常重要的一部分。了解和获取HTTP状态码有助于我们更好地处理接口返回的数据。本文将介绍使用JavaScript获取HTTP状态码的方法,并提供具体代码示例。一、什么是HTTP状态码HTTP状态码是指当浏览器向服务器发起请求时,服务

See all articles