js中settimeout()的用法详解_低噪放工作原理

js中settimeout()的用法详解_低噪放工作原理基本原理setTimeout(func,delay,args):设置超时调用,经过delay时间后,将func函数加入到执行队列中准备调用。如果队列为空,立即执行该函数,否则等待线程空闲再执行。setInterval(func,interval,args):设置…

大家好,又见面了,我是你们的朋友全栈君。如果您正在找激活码,请点击查看最新教程,关注关注公众号 “全栈程序员社区” 获取激活教程,可能之前旧版本教程已经失效.最新Idea2022.1教程亲测有效,一键激活。

Jetbrains全系列IDE稳定放心使用

setTimeout与setInterval概述

setTimeout与setInterval是JavaScript引擎提供的两个定时器方法,分别用于函数的延时执行和循环调用。前者的主要思想是通过一个定时器,让函数在计时结束后再执行;后者则是每隔一定的时间,就启动一次函数的执行。

从原理来看,两者似乎并不复杂。但由于JavaScript引擎是单线程的,这就让上述两个定时器的实际执行变得稍微复杂了一些。下面我们来看一下两者的运行机制与需要注意的问题。

基本原理

知识铺垫

单线程模型:由于JavaScript被设计为用在浏览器环境,而该环境下存在大量可能发生冲突的DOM操作,为了避免进行复杂的冲突处理(可能存在的冲突数量几乎不可预测),JavaScript的设计者舍弃了java的多线程模型(该模型下,执行引擎同时可以做几件事,但要进行线程同步),将其设计成了一门单线程语言(执行引擎在同一时间只做一件事)。

注意:这里的单线程是指JavaScript的主线程只有一个。除了这个主线程,JavaScript还有一个I/O线程,通过事件循环来处理I/O问题,但两者之间相对独立,不需要进行状态同步,因此我们仍然可以把JavaScript看成一门单线程语言。

任务队列:所谓任务队列,就是用于存储等待执行的任务的队列。由于JavaScript是一门单线程语言,如果当前有一个任务需要执行,但JavaScript引擎正在执行其他任务,那么这个任务就需要放进一个队列中进行等待。等到线程空闲时,就可以从这个队列中取出最早加入的任务进行执行(类似于我们去银行排队办理业务。单线程相当于说这家银行只有一个服务窗口,一次只能为一个人服务,后面到的就需要排队,而任务队列就是排队区,先到的就优先服务)。

注意:如果当前线程空闲,并且队列为空,那每次加入队列的函数将立即执行。

setTimeout与setInterval

setTimeout(func, delay, args):设置超时调用。如对于setTimeout(func, 100, args),js引擎会为func函数设置一个计时器,100毫秒后,将func添加到任务队列等待执行。

setInterval(func, interval, args):设置循环调用。对于语句setInterval(func, 100, args),js引擎每隔100毫秒就会把func添加到任务队列一次。

相同点:

  1. 两者都会加入同一个队列,等待线程空闲时执行。
  2. 两者都无法保证在何时执行回调,因为无法知道线程何时空闲。

不同点

  1. setTimeout只会将函数添加到任务队列一次,而setInterval则是循环往队列中添加函数。
  2. setTimeout可以保证函数在指定的时间间隔内不会执行,而setInterval无法保证(有可能出现接近连续执行的情况,后面会分析原因)。

运行机制

setTimeout

setTimeout的运行机制相对简单,即在执行该语句时,设置一个定时器,定时时间置为所设置的延时,当计时结束后,将传入的函数加入任务队列,之后的执行就交给任务队列负责。

setTimeout函数本身会返回一个句柄,我们可以在函数执行前通过向clearTimeout传入该句柄取消函数的执行。示例代码如下:

function func(message){ 
   
	alert(message);
}
//设置100毫秒后执行func函数
var timer = setTimeout(func, 100, "你好");

function cancel(){ 
   
	clearTimeout(timer);   //取消超时调用
}

上述代码将在100毫秒后执行func函数,弹出一个内容为”你好”的对话框。如果在100毫秒内调用了cancel,就可以取消func函数的执行。

setInterval

setInterval本质上就是每隔一定的时间向任务队列添加回调函数。但setInterval有一个原则:在向队列中添加回调函数时,如果队列中存在之前由其添加的回调函数,就放弃本次添加(不会影响之后的计时)。另外也可以通过clearInterval方法移除定时器,使用方法同clearTimeout。

由于setInterval只负责定时向队列中添加函数,而不考虑函数的执行,那么我们考虑一下下面的情况:

假设线程执行完setInterval(func, 100, args)后处于完全空闲状态(即只要向任务队列添加函数就会立即执行)。而func是一个相对复杂的函数,执行该函数需要90毫秒。那么函数的执行过程就会变成下图所示:
在这里插入图片描述
从图中可以看到,从上次函数执行完毕,到下次开始执行,之间只间隔了10毫秒,而不是我们所希望的每隔100毫秒执行一次(因为setInterval只关注任务添加,不关注任务执行)。

由于上述机制,在很多情况下,setInterval都会遇到一些性能问题。就拿上面的例子来说,我们的本意可能是每隔100毫秒执行一次函数,结果只等待了10毫秒就又执行了一次。另外,对于复杂的实际情况,setInterval经常出现两次的执行间隔相差甚远的情况,对于用户能感知到的操作,这会带来很不好的用户体验。因此在实际编码中,开发者通常会使用setTimeout来模拟实现setInterval效果(下面会有举例)。

而如果线程一开始是繁忙的,直到150毫秒处才进入空闲状态(假设func执行时长为10毫秒),那么实际的运行将变成下图所示:
在这里插入图片描述
这里在100毫秒处向队列添加func时,由于线程繁忙,上次添加的func还在队列中等待,因此直接丢弃本次要添加的函数,但在200毫秒时仍然重新向队列中添加func。

应用场景

setTimeout

setTimeout主要用于需要进行延时调用的场景中。如之前一篇文章介绍的js基础之函数的节流与防抖,就是setTimeout典型的应用场景。此外,由于setInterval存在的性能问题,在实际的编码中,开发人员通常会使用setTimeout来模拟setInterval,以防止出现函数连续执行的情况。如对于下面的代码:

function func(args){ 
   
  //函数本身的逻辑
  ...
}
var timer = setInterval(func, 100, args);

我们可以通过以下代码来实现:

var timer;
function func(args){ 
   
  //函数本身的逻辑
  ...
  //函数执行完后,重置定时器
  timer = setTimeout(func, 100, args);
}
timer = setTimeout(func, 100, args);

利用setTimeout保证在指定的时间内不会执行的特点,我们可以在执行完上次的回调函数后,重置定时器,实现循环执行func的效果,并且从上次执行完毕到下次执行开始,至少会经过100毫秒。这在实际的编码中通常会带来较大的性能提升,同时函数的执行间隔也会相对稳定。

setInterval

尽管存在上述性能问题,setInterval的使用场景相对较少,但当所使用的接口来自外部(即回调函数本身无法修改)时,就必须通过setInterval来实现循环执行了。此外,对于动画效果来说,我们通常会希望动画运行的更加平滑(也就是希望函数运行得更频繁),这时使用setInterval往往更加流畅,具体请参考之前的文章使用原生js实现简单动画效果

除了这类情况,开发者一般不会使用setInterval方法进行循环调用。

补充说明

setTimeout与setInterval的第一个参数可以是一个匿名函数,也可以是一个函数名,或者是一个字符串,如下面的写法都是合法的:

function func(msg){ 
   
  ...
}
//传入回调函数名
setTimeout(func, 100, "夕山雨");
//传入匿名函数
setTimeout(function(name){ 
   
  ...
}, 100, "夕山雨");
//传入字符串,js引擎会将其解析为函数体
setTimeout("alert('夕山雨')", 100);

但是传入如下的格式就可能报错:

setTimeout(func("夕山雨"), 100);

因为这种写法实际上是先调用func函数,然后再将返回值添加到任务队列。如果func的返回值不是函数(或可执行的字符串),那么程序就会报错;如果返回值是函数,则会将返回的函数添加到任务队列。该情况可以写成下面的形式:

//将其作为字符串传入,就可以被正确解析
setTimeout("func('夕山雨')", 100)

此外,当给setTimeout传入的延迟时间为0时,并不代表回调函数会立即执行。实际上浏览器规定的有一个默认的最短计时时间,对于现代浏览器,这个时间一般为4毫秒(老版本的浏览器则会更长一些)。也就是说,即使传入的延迟时间为0,浏览器也会至少在4毫秒后才会执行。

上述补充说明同样适用于setInterval。

总结

setTimeout与setInterval都是通过一个定时器控制回调函数的执行,但由于javascript单线程的特点,两者都不能准确控制函数的执行时间点,这点还请开发者注意。如果函数只需要执行一次,很显然我们会使用setTimeout来实现;如果是循环执行的情况,如果我们希望函数执行频率不那么高,并且间隔更稳定,通常是使用setTimeout模拟实现setInterval效果。

总的来说,虽然都被用于函数延迟执行,但两者的运行机制有本质上的区别,所以在使用的时候请注意区分。

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请联系我们举报,一经查实,本站将立刻删除。

发布者:全栈程序员-站长,转载请注明出处:https://javaforall.net/186042.html原文链接:https://javaforall.net

(0)
全栈程序员-站长的头像全栈程序员-站长


相关推荐

  • Python 冒泡排序_python

    Python 冒泡排序_python要学习冒泡排序必须知道它的原理:冒泡排序算法的原理如下:比较相邻的元素。如果第一个比第二个大,就交换他们两个。对每一对相邻元素做同样的工作,从开始第一对到结尾的最后一对。在这一点,最后的元素应该会是最大的数。针对所有的元素重复以上的步骤,除了最后一个。持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较。例子:1,2,3,4,5,6就拿1到6来举例子吧!这里面有n个数字,你要对其进…

    2022年10月16日
    0
  • sql语法:inner join on, left join on, right join on具体用法

    sql语法:inner join on, left join on, right join on具体用法

    2021年12月15日
    62
  • vim 支持python_如何进入python

    vim 支持python_如何进入pythonLinux 下的 python 虚拟环境 + vim快捷方式

    2022年4月22日
    56
  • 大数据平台展示可视化效果,echarts图表实战项目(源码50套)「建议收藏」

    大数据平台展示可视化效果,echarts图表实战项目(源码50套)「建议收藏」最近接了个任务需要用H5在前台两个大电视上做两页数据展示公司的产品数据,效果要高大上,充分展示咱们公司的实力,给各位来公司参观的大能们留下深刻的印象。还好之前接触过HTML5,所以第一时间想到就是echarts,这个框架对于数据展示尤其图表类处理的还是非常强大和炫酷的。说干就干,首先到官网上把框架下下来,各组件Demo和API都熟悉下,对于你想要的东西和效果心里有个底,就开始动工了。官网地址是:https://echarts.apache.org,里面的Demo都是代码和效果图文并貌,还可以在线修改

    2022年10月12日
    0
  • Linux-nmap命令使用

    Linux-nmap命令使用用namp对局域网扫描一遍,然后查看arp缓存表就可以知道局域内ip-mac的对应了namp比较强大也可以直接扫描mac地址和端口进行ping扫描,打印出对扫描做出响应的主机:  nmap-sP192.168.1.0/24仅列出指定网络上的每台主机,不发送任何报文到目标主机:  nmap-sL192.168.1.0/24  探测目标主机开放的端口,可以指定一个以…

    2022年5月13日
    34
  • 申请测试微信公众号流程_如何申请免测

    申请测试微信公众号流程_如何申请免测申请测试微信公众号申请的地址:https://mp.weixin.qq.com/debug/cgi-bin/sandboxinfo?action=showinfo&t=sandbox/ind

    2022年8月5日
    5

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注

关注全栈程序员社区公众号