JavaScript中的setTimeout和setInterval
上一篇博文<浏览器中Javascript单线程分析>中描述了浏览器中Javascript单线程的原理。
在此基础上,这篇文章将主要介绍setTimeout/setInterval是如何模拟异步的,且二者之间又有何区别。
首先我们来分析它们如何模拟异步。
可以根据上篇博文了解到JS引擎内部维护一个队列,用来存放各种回调函数,其中也包括setTimeout/setInterval回调。
下面用代码结合图形的方式来描述异步是如何产生的。
先看例1:
<html>
<body>
<script>
setTimeout(function(){
console.log(1);
},0);
console.log(2);
</script>
</body>
</html>
输出结果为:2 1
其队列可描述为:

其中code snippet指<script>里的逻辑代码段;setTimeout callback指定时器回调。
虽然setTimeout设为0ms,但当前有代码段在执行,只能将回调置于队列后面。
再看例2:
<html>
<body>
<script>
setInterval(function(){
console.log(1);
},0);
console.log(2);
</script>
</body>
</html>
输出结果为:2 1 1 1 1 ...
其队列可描述为:

至于为什么不是 1 1 1 ... 2,原理和setTimeout(..,0)是一致的。
从上看到通过回调队列实现了延时函数setTimeout/setInterval。
那么问题来了,这两个函数的延时是否精确,也就是说是否按照设置的延时时间执行?
接着看例3 setTimout:
<html>
<body>
<script>
setTimeout(function(){
console.log(1);
},1000);
//耗时处理超过1s,假设约2s
doSomething();
</script>
</body>
</html>
结果:延迟输出1会在doSomething()执行后大约2s后执行。
这是为什么,我们可以用上一篇博客的原理来解释,还是用队列图来描述:

再来看例4 setInterval:
<html>
<body>
<script>
setInterval(function(){
console.log(1);
},10);
//耗时处理超过1s,假设约2s
doSomething();
</script>
</body>
</html>
结果:延迟输出第一个1会在doSomething()执行后大约2s后执行,紧接着马上输出第二个1,时间间隔并没有10ms。
之所以间隔没有10ms,是因为定时回调在插入到队列时发现预期时间点被doSomething占用,只能后延插入的回调,
这样会导致第二次定时回调会直接放在第一个定时回调后,所以在执行时没有间隔。
队列图如下:

从上面的结果可以回答我们之前提出的疑问,setTimout/setInterval不能保证一定在延时达到时执行回调。
既然不能保证延时时间,那产生上面的结果的原因又是什么呢?且两个函数在延时上有什么区别呢?
让我们来看例5:
<html>
<body>
<script>
setTimeout(function(){
//耗时函数,假设为2s
doSomething();
setTimout(arguments.callee,1000);
},1000);
setInterval(function(){
//耗时函数,假设为2s
doSomething();
},1000);
</script>
</body>
</html>
从逻辑意义上,如果setTimeout/setInterval延时没有偏移,那么两段断码意义一致。
但从上面实验结果表明二者均存在延时偏移,那么偏移是怎么产生的,且二者偏移有何不同?
还是用两个队列来描述着两段代码的执行结果。
setTimeout:

setInterval:

从上我们便可以清楚地看到,setTimeout每次递归会等待上一次回调函数执行完成,而回调中存在耗时函数,所以会超过1s才能执行下一次回调;
而setInterval不会等待回调执行完成,而是将回调每隔1s插入到回调队列中,如果该时间点存在其他回调,则该时间点的回调往后移动,后面的定时回调时间点不受影响,
因为回调中存在耗时函数,所以定义器不能刚好将回调插入预设的时间点上,而只能插入到耗时函数后,这会导致后面的定时回调函数堆积,执行时时间间隔不会超过1s。
图中层叠关系代表定时回调函数堆积(有序)。
总的来说,也就是setTimeout延时间隔可能大于设置的时间,而setInterval延时间隔可能小于设置的时间。
关于浏览器单线程原理和setTimout/setInterval原理就介绍到这。
JavaScript中的setTimeout和setInterval的更多相关文章
- JavaScript中的setTimeout、setInterval和随机函数制作简易抽奖小程序
		几乎所有计算机语言有都内置随机函数.当然这种随机,人们习惯称为伪随机数发生器,产生的是一个[0,1)之间的一个小数.再通过简单算术运算生成一个符合需求的整数.JS中通用公式通常为parseInt(Ma ... 
- Javascript定时器(二)——setTimeout与setInterval
		一.解释说明 1.概述 setTimeout:在指定的延迟时间之后调用一个函数或者执行一个代码片段 setInterval:周期性地调用一个函数(function)或者执行一段代码. 2.语法 set ... 
- js中的setTimeout和setInterval
		在html页面中要使用自动刷新功能时,可以是使用js中setTimeout和setInterval: 一.使用方法 setTimeout的使用setTimeout('要调用的Js方法', 调用的延迟时 ... 
- JavaScript 如何使用 setTimeout 实现 setInterval
		JavaScript 如何使用 setTimeout 实现 setInterval website multi content page setIntervalSimulator "use ... 
- JavaScript定时机制setTimeout与setInterval研究
		JavaScript的setTimeout与setInterval是两个很容易欺骗别人感情的方法,因为我们开始常常以为调用了就会按既定的方式执行, 我想不少人都深有同感, 例如 setTimeout( ... 
- 关于js中的setTimeout和setInterval
		http://ejohn.org/blog/how-javascript-timers-work 这是John的一篇博文说到setTimeout和setInterval的区别,在看js高效图形编程的时 ... 
- QML中实现setTimeout和setInterval
		Qt的QML中,js未提供setTimeout和setInterval,可以通过下面的代码实现. Timer {id: timer} function setTimeout(cb,delayTime) ... 
- 关于JavaScript中的setTimeout()链式调用和setInterval()探索
		http://www.cnblogs.com/Wenwang/archive/2012/01/06/2314283.html http://www.cnblogs.com/yangjunhua/arc ... 
- JavaScript定时器:setTimeout()和setInterval()
		1 超时调用setTimeout() 顾名思义,超时调用的意思就是在一段实际之后调用(在执行代码之前要等待多少毫秒) setTimeout()他可以接收两个参数: 1 要执行的代码或函数 2 毫秒(在 ... 
随机推荐
- 【转】linux network namespace 学习
			原文地址:https://segmentfault.com/a/1190000004059167 介绍 在专业的网络世界中,经常使用到Virtual Routing and Forwarding(VR ... 
- 【转载】Android端手机测试体系
			1.冒烟测试 跟web端 的测试流程一样,你拿到一个你们开发做出来的apk首先得去冒烟,也就是保证他的稳定性,指定时间内不会崩溃.这款原生sdk自带的monkey可以当做 我们的测试工具.就跟我之前博 ... 
- Codeforces 653D Delivery Bears(最大流)
			题目大概说有一张n个点m条边的简单有向图,每条边只能允许一定总量的货物通过.要让x只熊从1点到n点运送货物,每只熊都要运送且运送的货物重量都一样,求该重量的最大值. 二分重量判断是否成立. 如果已知重 ... 
- "Accepted today?"[HDU1177]
			"Accepted today?" Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (J ... 
- Hadoop维护IPC链接
			IPC链接上长时间没有发生远程调用,客户端会发送一个心跳消息给服务器端,用于维护链接. Connection的lastActivity用来记录上次发生IPC通信的时间. Connection.touc ... 
- Iterable(迭代器)的用法
			一.前言 在开发中,经常使用的还是for-each循环来遍历来Collection,不经常使用Iterable(迭代器)的,下面记录一下terable是一般用法: 二.说明 迭代器是一种设计模式,它是 ... 
- 使用Percona Toolkit解决Mysql主从不同步问题【备忘】
			由于各种原因,mysql主从架构经常会出现数据不一致的情况出现,大致归结为如下几类 1:备库写数据 2:执行non-deterministic query 3:回滚掺杂事务表和非事务表的事务 4:bi ... 
- POJ 2420 A Star not a Tree?(模拟退火)
			题目链接 居然1Y了,以前写的模拟退火很靠谱啊. #include <cstdio> #include <cstring> #include <string> #i ... 
- 通过Ajax post Json类型的数据到Controller
			View function postSimpleData() { $.ajax({ type: "POST", url: "/Service/SimpleData&quo ... 
- POJ 1260  Pearls    简单dp
			1.POJ 1260 2.链接:http://poj.org/problem?id=1260 3.总结:不太懂dp,看了题解 http://www.cnblogs.com/lyy289065406/a ... 
