setTimeout,setInterval运行原理

 
function a() {
    setTimeout(function(){alert(1)},0);
    alert(2);
}
a();

 

和其余的编程语言同样,Javascript中的函数调用也是经过堆栈实现的。在执行函数a的时候,a先入栈,若是不给alert(1)加setTimeout,那么alert(1)第2个入栈,最后是alert(2)。但如今给alert(1)加上setTimeout后,alert(1)就被加入到了一个新的堆栈中等待,并”尽量快“的执行。这个尽量快就是指在a的堆栈完成后就马上执行,所以实际的执行结果就是先alert(2),再alert(1)。在这里setTimeout其实是让alert(1)脱离了当前函数调用堆栈。 
利用这一点,javascript其实是有多个线程的。
问题是: 多个setTimeout 是 分别放在不一样的堆栈吗?若是是,那么咱们就真的有了多线程的基础了

另外一篇文章中的阐述。哪一个是对的呢?javascript

javascript线程解释(setTimeout,setInterval你不知道的事)

2010年8月10日
 
今天看到这篇文章,学到了很多东西 特此发出来 和你们分享
JavaScript的setTimeout与setInterval是两个很容易欺骗别人感情的方法,由于咱们开始经常觉得调用了就会按既定的方式执行, 我想很多人都深有同感,例如
setTimeout( function(){ alert(’你好!’); } , 0);
setInterval( callbackFunction , 100);

 

认为setTimeout中的问候方法会当即被执行,由于这并非凭空而说,而是JavaScript API文档明肯定义第二个参数意义为隔多少毫秒后,回调方法就会被执行. 这里设成0毫秒,理所固然就当即被执行了.
同理对setInterval的callbackFunction方法每间隔100毫秒就当即被执行深信不疑!
但随着JavaScript应用开发经验不断的增长和丰富,有一天你发现了一段怪异的代码而百思不得其解:
div.onclick = function(){
    setTimeout( function(){document.getElementById(’inputField’).focus();}, 0);
};

 

既然是0毫秒后执行,那么还用setTimeout干什么, 此刻, 坚决的信念已开始动摇.
直到最后某一天 , 你不当心写了一段糟糕的代码:
setTimeout( function(){ while(true){} } , 100);
setTimeout( function(){ alert(’你好!’); } , 200);
setInterval( callbackFunction , 200);

 

第一行代码进入了死循环,但不久你就会发现,第二,第三行并非预料中的事情,alert问候未见出现,callbacKFunction也杳无音讯!
这时你完全迷惘了,这种情景是难以接受的,由于改变长久以来既定的认知去接受新思想的过程是痛苦的,可是事实摆在眼前,对JavaScript真理的探求并不会由于痛苦而中止,下面让咱们来展开JavaScript线程和定时器探索之旅!
拔开云雾见月明
出现上面全部误区的最主要一个缘由是: 潜意识中认为,JavaScript引擎有多个线程在执行,JavaScript的定时器回调函数是异步执行的
而事实上的,JavaScript使用了障眼法,在多数时候骗过了咱们的眼睛,这里得澄清一个事实:
<strong>JavaScript引擎是单线程运行的,浏览器不管在何时都有且只有一个线程在运行JavaScript程序.
</strong>

 

JavaScript引擎用单线程运行也是有意义的,单线程没必要理会线程同步这些复杂的问题,问题获得简化
那么单线程的JavaScript引擎是怎么配合浏览器内核处理这些定时器和响应浏览器事件的呢?下面结合浏览器内核处理方式简单说明html

浏览器内核实现容许多个线程异步执行,这些线程在内核制控下相互配合以保持同步。假如某一浏览器内核的实现至少有三个常驻线程:javascript引擎线程,界面渲染线程,浏览器事件触发线程,除些之外,也有一些执行完就终止的线程,如Http请求线程,这些异步线程都会产生不一样的异步事件,下面经过一个图来阐明单线程的JavaScript引擎与另外那些线程是怎样互动通讯的,虽然每一个浏览器内核实现细节不一样,但这其中的调用原理都是大同小异java

由图可看出,浏览器中的JavaScript引擎是基于事件驱动的,这里的事件可看做是浏览器派给它的各类任务,这些任务能够源自 JavaScript引擎当前执行的代码块(如调用setTimeout添加一个任务),也可来自 浏览器内核的其它线程(如界面元素鼠标点击事件,定时触发 器时间到达通知,异步请求状态变动通知等)。从代码角度看任务实体就是各类回调函数,JavaScript引擎一直等待着任务队列中任务的到来,因为单线程关系,这些任务得进行排队,一个接着一个被引擎处理。
上图t1-t2...tn表示不一样的时间点,tn下面对应的小方块表明该时间点的任务,假设如今是t1时刻,JavaScript引擎运行在t1对应的任务方块代码内,在这个时间点内,咱们来描述一下浏览器内核其它线程的状态。
 
t1时刻:
GUI渲染线程:
该线程负责渲染浏览器界面HTML元素,当界面须要重绘(Repaint)或因为某种操做引起回流(reflow)时,该线程就会执行。本文虽然重点解释JavaScript定时机制,但这时有必要说说渲染线程,由于该线程与JavaScript引擎线程是互斥的,这容易理解,由于JavaScript脚本是可操纵DOM元素,在修改这些元素属性同时渲染界面,那么渲染线程先后得到的元素数据就可能不一致了。
在JavaScript引擎运行脚本期间,浏览器渲染线程都是处于挂起状态的,也就是说被”冻结”了.

因此,在脚本中执行对界面进行更新操做,如添加结点,删除结点或改变结点的外观等更新并不会当即体现出来,这些操做将保存在一个队列中,待JavaScript引擎空闲时才有机会渲染出来。ajax

GUI事件触发线程:
JavaScript脚本的执行不影响html元素事件的触发,在t1时间段内,首先是用户点击了一个鼠标键,点击被浏览器事件触发线程捕捉后造成 一个鼠标点击事件,由图可知,对于JavaScript引擎线程来讲,这事件是由其它线程异步传到任务队列尾的,因为引擎正在处理t1时的任务,这个鼠标点击事件正在等待处理。
定时触发线程:
注意这里的浏览器模型定时计数器并非由JavaScript引擎计数的,由于JavaScript引擎是单线程的,若是处于阻塞线程状态就计不了时,它必须依赖外部来计时并触发定时,因此队列中的定时事件也是异步事件。
由图可知,在这t1的时间段内,继鼠标点击事件触发后,先前已设置的setTimeout定时也到达了,此刻对JavaScript引擎来讲,定时触发线程产生了一个异步定时事件并放到任务队列中,该事件被排到点击事件回调以后,等待处理。
同理,仍是在t1时间段内,接下来某个setInterval定时器也被添加了,因为是间隔定时,在t1段内连续被触发了两次,这两个事件被排到队尾等待处理。
可见,假如时间段t1很是长,远大于setInterval的定时间隔,那么定时触发线程就会源源不断的产生异步定时事件并放到任务队列尾而无论它们是否已被处理,而一旦t1和最早定时事件前面的任务已处理完,这些排列中的定时事件就依次不间断的被执行,这是由于,对于JavaScript引擎来讲,在处理队列中的各任务处理方式都是同样的,只是处理的次序不一样而已。
t1事后,也就是说当前处理的任务已返回,JavaScript引擎会检查任务队列,发现当前队列非空,就取出t2下面对应的任务执行,其它时间依此类推,由此看来:
若是队列非空,引擎就从队列头取出一个任务,直到该任务处理完,即返回后引擎接着运行下一个任务,在任务没返回前队列中的其它任务是无法被执行的.

 

相信您如今已经很清楚JavaScript是否可多线程,也理解JavaScript定时器运行机制了,下面咱们来对一些案例进行分析编程

案例1:setTimeout与setInterval浏览器

setTimeout(function(){
   /* 代码块... */
   setTimeout(arguments.callee, 10);
}, 10);
 
setInterval(function(){
   /*代码块... */
 }, 10);

 

这两段代码看一块儿效果同样,其实非也多线程

第一段中回调函数内的setTimeout是JavaScript引擎执行后再设置新的setTimeout 定时(此时会把代码插入任务队列),假定上一个回调处理完到下一个回调开始处理为一个时间间隔,理论两个setTimeout回调执行时间间隔>=10ms。setTimeout好处就是严格保证每隔一段时间执行代码。异步

第二段自setInterval设置定时后,定时触发线程就会源源不断的每隔十秒产生异步定时事件并放到任务队列尾,理论上两个setInterval 回调执行时间间隔<=10,这就可能致使上一段代码执行完毕后当即执行下一段代码,不会出现想象中的间隔一段时间再执行编程语言

案例2:ajax异步请求是否真的异步?
不少同窗朋友搞不清楚,既然说JavaScript是单线程运行的,那么XMLHttpRequest在链接后是否真的异步?
其实请求确实是异步的,不过这请求是由浏览器新开一个线程请求(参见上图),当请求的状态变动时,若是先前已设置回调,这异步线程就产生状态变动事件放到 JavaScript引擎的处理队列中等待处理,当任务被处理时,JavaScript引擎始终是单线程运行回调函数,具体点即仍是单线程运行 onreadystatechange所设置的函数.
 
补充:
你们知道setTimeout,setInterval接受两个参数,一个是回调函数,另外一个是时间参数,其实这两个函数还接受第三个,第四个参数,这些参数就是回调函数的参数
setTimeout(function(a,b,c){
    console.log(a);//a
    console.log(b);//b
    console.log(c);//c
},0,3,4,5);
 
setInterval(function(a,b,c){
    console.log(a);//b
    console.log(b);//b
    console.log(c);//c
},0,3,4,5);

 

相关文章
相关标签/搜索