上一篇博文<浏览器中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的更多相关文章

  1. JavaScript中的setTimeout、setInterval和随机函数制作简易抽奖小程序

    几乎所有计算机语言有都内置随机函数.当然这种随机,人们习惯称为伪随机数发生器,产生的是一个[0,1)之间的一个小数.再通过简单算术运算生成一个符合需求的整数.JS中通用公式通常为parseInt(Ma ...

  2. Javascript定时器(二)——setTimeout与setInterval

    一.解释说明 1.概述 setTimeout:在指定的延迟时间之后调用一个函数或者执行一个代码片段 setInterval:周期性地调用一个函数(function)或者执行一段代码. 2.语法 set ...

  3. js中的setTimeout和setInterval

    在html页面中要使用自动刷新功能时,可以是使用js中setTimeout和setInterval: 一.使用方法 setTimeout的使用setTimeout('要调用的Js方法', 调用的延迟时 ...

  4. JavaScript 如何使用 setTimeout 实现 setInterval

    JavaScript 如何使用 setTimeout 实现 setInterval website multi content page setIntervalSimulator "use ...

  5. JavaScript定时机制setTimeout与setInterval研究

    JavaScript的setTimeout与setInterval是两个很容易欺骗别人感情的方法,因为我们开始常常以为调用了就会按既定的方式执行, 我想不少人都深有同感, 例如 setTimeout( ...

  6. 关于js中的setTimeout和setInterval

    http://ejohn.org/blog/how-javascript-timers-work 这是John的一篇博文说到setTimeout和setInterval的区别,在看js高效图形编程的时 ...

  7. QML中实现setTimeout和setInterval

    Qt的QML中,js未提供setTimeout和setInterval,可以通过下面的代码实现. Timer {id: timer} function setTimeout(cb,delayTime) ...

  8. 关于JavaScript中的setTimeout()链式调用和setInterval()探索

    http://www.cnblogs.com/Wenwang/archive/2012/01/06/2314283.html http://www.cnblogs.com/yangjunhua/arc ...

  9. JavaScript定时器:setTimeout()和setInterval()

    1 超时调用setTimeout() 顾名思义,超时调用的意思就是在一段实际之后调用(在执行代码之前要等待多少毫秒) setTimeout()他可以接收两个参数: 1 要执行的代码或函数 2 毫秒(在 ...

随机推荐

  1. SlidesJS的使用

    项目中对slideshow要求要有触屏滑动换图功能,就想到了SlidesJS这个Jquery插件 例排,先把静态html写好 <div id="cm_slides"> ...

  2. iOS学习22之视图控制器

    1.自定义视图 1> 概述   定义视图:系统标准UI之外,自己组合而出的新的视图. 定义视图的优点: iOS提供了很多UI组件,借助它们我们可以实现不同的功能.尽管如此,实际开发中,我们还需要 ...

  3. 树形DP水题杂记

    BZOJ1131: [POI2008]Sta 题意:给出一个N个点的树,找出一个点来,以这个点为根的树时,所有点的深度之和最大. 题解:记录每个点的深度,再根据根节点的深度和逐层推导出其他点的深度和. ...

  4. 洛谷 P1341 无序字母对 Label:欧拉路 一笔画

    题目描述 给定n个各不相同的无序字母对(区分大小写,无序即字母对中的两个字母可以位置颠倒).请构造一个有n+1个字母的字符串使得每个字母对都在这个字符串中出现. 输入输出格式 输入格式: 第一行输入一 ...

  5. JS调用水晶报表打印翻页按钮事件

    默认的水晶报表打印按钮.翻页按钮太小,并且样式不好调整,考虑自己做一个按钮,然后调用水晶报表的按钮事件. 在实际操作中发现可以在.net按钮的服务器端事件中调用翻页方法: CrystalReportV ...

  6. C++的STL中vector内存分配方法的简单探索

    STL中vector什么时候会自动分配内存,又是怎么分配的呢? 环境:Linux  CentOS 5.2 1.代码 #include <vector> #include <stdio ...

  7. Redis在windows下的安装使用

    下载的windows版本是redis-2.0.2,解压到D盘下: D:\redis-2.0.2 启动Redis服务(conf文件指定配置文件,若不指定则默认): D:\redis-2.0.2>r ...

  8. 关于Autoit上传文件的问题

    Autoit上传文件需要安装两个软件: 使用这个做文件上传时:在代码中需要添加的代码如下: Runtime.getRuntime().exec("d:/zhpg.exe"); D盘 ...

  9. [Android] adb.exe换了位置

    好久没有做android开发了,今天重新下载了新的sdk,发现adb.exe从sdk/tools里面消失了,添加了系统环境变量的路径就还是没法调动adb.exe命令,网上搜了一下原理是存在了新版的sd ...

  10. 优化MySchool总结习题

    1) 在SQL Server 中,为数据库表建立索引能够( C ). A. 防止非法的删除操作 B. 防止非法的插入操作 C. 提高查询性能 D. 节约数据库的磁盘空间 解析:索引的作用是通过使用索引 ...