上一篇博文<浏览器中Javascript单线程分析>中描述了浏览器中Javascript单线程的原理。html
在此基础上,这篇文章将主要介绍setTimeout/setInterval是如何模拟异步的,且两者之间又有何区别。浏览器
首先咱们来分析它们如何模拟异步。异步
能够根据上篇博文了解到JS引擎内部维护一个队列,用来存放各类回调函数,其中也包括setTimeout/setInterval回调。函数
下面用代码结合图形的方式来描述异步是如何产生的。spa
先看例1:线程
<html> <body> <script> setTimeout(function(){ console.log(1); },0); console.log(2); </script> </body> </html>
输出结果为:2 13d
其队列可描述为:code
其中code snippet指<script>里的逻辑代码段;setTimeout callback指定时器回调。htm
虽然setTimeout设为0ms,但当前有代码段在执行,只能将回调置于队列后面。blog
再看例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原理就介绍到这。