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效果。

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

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

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

【正版授权,激活自己账号】: Jetbrains全家桶Ide使用,1年售后保障,每天仅需1毛

【官方授权 正版激活】: 官方授权 正版激活 支持Jetbrains家族下所有IDE 使用个人JB账号...

(0)


相关推荐

  • mongodb导入json_json格式是什么

    mongodb导入json_json格式是什么刚开始接触mongodb,以下介绍使用mongoVUE来导入和导出json格式的数据1、导出瞬间导出到指定的text文件中,我们用文本编辑器打开预览2、导入导入的时候首先我我们要选择导入的表,点进去之后可以直接写json文本也可以导入,txt文件不过我们刚刚导出的json格式的txt文件是不能直接导入的,稍作一下修改,(需要的话去掉_id),去掉每个json串之间…

  • PCA最小平方误差理论推导

    PCA最小平方误差理论推导PCA求解其实是寻找最佳投影方向,即多个方向的标准正交基构成一个超平面。理论思想:在高维空间中,我们实际上是要找到一个d维超平面,使得数据点到这个超平面的距离平方和最小

    2021年12月30日
  • 学习Python之路之ipython的使用及Pycharm的安装[通俗易懂]

    学习Python之路之ipython的使用及Pycharm的安装[通俗易懂]一、Python的基础知识1、概念Python是一种面向对象的解释型计算机程序设计语言,由荷兰人GuidovanRossum于1989年发明,第一个公开发行版发行于1991年。2、Python的特点优点:简单、优雅、明确 有强大的第三方库模块 可跨平台移植 一种面向对象的语言缺点:代码执行速度慢,相比C语言,不过现在python的异步并发框架导致执行速度慢 p…

  • 心血漏洞(OpenSSL升级)[通俗易懂]

    心血漏洞(OpenSSL升级)[通俗易懂]查看系统OpenSSL版本:opensslversion,查看该版本是否存在心血漏洞,受影响版本为:1.0.1—1.0.1f/1.0.2Beta1下载最新的OpenSSL安装包并上传到服务器下载地址:https://www.openssl.org/安装依赖:yuminstall-ygcczlib-develpam-devel开启telnet服务yum-yinstalltelnet-serversed-i”s/yes/no/g”/etc/xine.

  • nodejs安装与环境变量配置[通俗易懂]

    nodejs安装与环境变量配置[通俗易懂]NodeJsNode.js简介发展史特性模块下载nodejs系统环境nodejs(msi)方式的安装nodejs(zip)方式的安装环境配置Linux下安装Node测试参考资料Node.js简介Node是一个让JavaScript运行在服务端的开发平台,它让JavaScript成为与PHP、Python、Perl、Ruby等服务端语言平起平坐的脚本语言。发布于2009年5月,由R…

  • PHP网站从Apache转移到Nginx后产生404错误的原因和解决办法

    PHP网站从Apache转移到Nginx后产生404错误的原因和解决办法

发表回复

您的电子邮箱地址不会被公开。

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