参考文章:Javascript 异步实现机制
同步:js是单线程的,所有任务只能顺序执行,一个完成之后才能进行下一个,这是js中的同步。
那js的异步是指什么?比如定时器。在一段代码中,如果有定时器,会先执行完所有的同步任务,再去执行定时器里的任务。需要等待的任务,先不执行,等其他任务执行完成之后再执行,这是异步。
var a = true;
setTimeout(function(){
a = false;
}, 100)
while(a){
console.log('while执行了')
}
上述代码,有些人可能会认为100ms以后,a会变成false,然后while循环就终止了。但实际上不是。因为js是单线程的,所以进入while循环之后,没有线程去跑定时器了
(注意,这个说法不够准确),所以这个代码是个死循环。
那js中的异步到底是怎么实现的?
但凡“ 即是单线程又是异步 ”的语言都有一个共同的特点:它们是 event-driven 的,所以 Javascript 异步的实现也与其事件机制关系密切。
在浏览器端
浏览器端的 Javascript 实现了两个很重要的异步 API,它们分别是 定时器 和 AJAX请求
定时器
JS线程在执行到定时器之后,会通知浏览的定时器线程开始计时。然后JS线程继续执行后续的同步任务,而定时器线程开始计时,在时间到达之后会将延时事件推入事件队列中。等到JS执行完所有的同步任务之后,就会顺序执行事件队列中的任务。
所以JS的定时器的延时其实是不准的,可能会超过延时时间的,它的准确的延时时间还依赖于后续同步任务执行的时间和事件队列排在它之前的任务的执行时间。
所以上面那段代码死循环的原因,不是定时器没有执行,定时器确实执行了,延时事件会被推入事件队列中,但是没有执行的机会,因为同步任务没有结束的点。
AJAX请求
JS线程在执行到ajax请求之后,会由浏览器的HTTP请求线程发起对服务器的请求,在请求得到响应之后触发请求完成事件,将回调函数推入事件队列等待执行。
DOM上的交互事件
当用户点击一个绑定点击处理函数的 DOM 元素时,会有一个点击事件排入事件队列,该点击事件也需要等到当前所有正在运行的代码结束之后(可能还要等待其它此前已排队的事件也依次结束),才会执行。
在NodeJS端
NodeJS中的异步I/O是由Libuv实现的。所以,当我们将 I/O 操作的请求传达给 Libuv 之后,Libuv 开启线程来执行这次 I/O 调用,并在执行完成后,传回给 Javascript 进行后续处理。
NodeJS 非常适合开发 IO 密集型应用,但并不适合开发 CPU(计算) 密集型应用.
NodeJS 异步的天性,在处理并发 IO 的时候不会阻塞主线程,实际上这种异步是借助于多线程实现的,IO 任务完成之后排队等待主线程执行。因为 NodeJS 主线程执行同步代码的速度非常之快,所以完全可以 hold 得住大规模的并发请求。
如果 NodeJS 主线程在执行同步任务的时候遇到一些计算量非常大,或者执行循环太久,等非常耗时的操作的时候,就会导致后续的代码以及事件队列里已完成的 IO 任务迟迟得不到执行,严重拖垮 NodeJS 的性能。
网友评论