1. setTimeout
我们看个例子:
var ms=0,el=0;
function test(){
console.log('main start');
ms=new Date().getTime();
//由于javascript是单线程的,因此setTimeout中排队的任务哪怕延迟0秒也要等线程执行完之后才能执行
setTimeout(()=>{
el=new Date().getTime()-ms;
console.info('setTimeout0 executed! actually '+el+'ms elapsed')
},0);
var i=999999999;
while ((i--)>0){
if(i%2===0){
if(Math.random()>=0.99999999)
{ console.log(i); }
}
}
console.log('main end');
}
test();
结果:
可以看到, 我们原本通过setTimeout计划立即执行的输出,实际上要等其所在的当前过程执行完后才执行,因此在花了近5.246秒执行完当前过程后执行setTimeout任务中的输出。
2. Ajax请求回调
理论上,如果ajax异步请求,它的异步回调函数是在单独一个线程中,那么回调函数必然不会被其他线程“阻挠”而顺利执行,也就是当前过程发出ajax请求之后哪怕陷入死循环,它的回调执行也不应该受到影响,可是实际情况并非如此,发出ajax请求后当前过程未执行完,回调函数无法执行,浏览器会因为死循环假死。
结论:根据实践结果,可以得出,javascript引擎确实是单线程处理它的任务队列的。在javascript里实现异步编程很大程度上就是一种障眼法。(https://blog.csdn.net/a312983516/article/details/7452587)
3. 单线程异步执行的JavaScript
在浏览器中,JavaScript引擎是单线程执行的。也就是说,在同一时间内,只能有一段代码被JavaScript引擎执行。页面加载时,JavaScript引擎会顺序执行页面上所有JavaScript代码,优先执行同步代码。而异步代码由事件触发引擎按照“事件发生”的顺序添加到JavaScript引擎的任务队列中,待所有同步代码执行结束后,JavaScript引擎会按照任务队列中的顺序来执行异步代码。(参考 JavaScript异步机制:https://www.cnblogs.com/zhaodongyu/p/3922961.html)
JavaScript引擎是单线程运行的,浏览器无论在什么时候都只且只有一个线程在运行JavaScript程序。
浏览器的内核是多线程的,它们在内核控制下相互配合以保持同步,一个浏览器至少实现三个常驻线程:JavaScript引擎线程,GUI渲染线程,浏览器事件触发线程。
- JavaScript引擎是基于事件驱动单线程执行的,JavaScript引擎一直等待着任务队列中任务的到来,然后加以处理,浏览器无论什么时候都只有一个JavaScript线程在运行JavaScript程序。
- GUI渲染线程负责渲染浏览器界面,当界面需要重绘(Repaint)或由于某种操作引发回流(Reflow)时,该线程就会执行。但需要注意,GUI渲染线程与JavaScript引擎是互斥的,当JavaScript引擎执行时GUI线程会被挂起,GUI更新会被保存在一个队列中等到JavaScript引擎空闲时立即被执行。
- 事件触发线程,当一个事件被触发时该线程会把事件添加到待处理队列的队尾,等待JavaScript引擎的处理。这些事件可来自JavaScript引擎当前执行的代码块如setTimeout、也可来自浏览器内核的其他线程如鼠标点击、Ajax异步请求等,但由于JavaScript的单线程关系所有这些事件都得排队等待JavaScript引擎处理(当线程中没有执行任何同步代码的前提下才会执行异步代码)。
4. 顺便值得一提的是setInterval
比如setInterval(fn, 10)
- 有可能某一次事件触发的时候,上一次事件的处理方法fn还没有机会得到执行,仍然在等待队列中,这个时候,这个新的定时器事件就被丢弃,继续开始下一次计时。
- 比如上一个定时器事件的处理方法触发之后,等待了5ms才获得被执行的机会,而第二个定时器事件的处理方法被触发之后,马上就被执行了。那么这两者之间的时间间隔实际上只有5ms。
(参考 JavaScript异步机制:https://www.cnblogs.com/zhaodongyu/p/3922961.html)
* 因此,setTimeout()、setInterval()并不适合实现精确的按固定间隔的调度操作。