javascript是单线程语言,那他的异步机制是怎么实现的?

javascript单线程就是在一个javascript运行环境中(如:浏览器,nodejs)只能同时运行一个javascript线程,那么单线程的语言的异步机制是如何实现的?
javascript是一个事件驱动的语言,他的异步机制与事件机制相关。

在javascript中,有一个主线程和一个异步队列池,并且异步队列池中的事件,需要等到主线程执行完毕才执行。

  • 主线程,就是执行所有js同步代码的线程。
  • 当js遇到异步操作时,会将异步操作推送到一个异步队列中,这个异步操作具体要做的事情,由js的宿主去执行,怎么理解呢?如浏览器发送一个ajax,js只需要将发送请求提交给浏览器,然后注册一个回调事件,浏览器会用多线程去发送真正的http请求,等到请求完成,再通知js异步队列中的指定回调,js再去通知代码中的回调,这样一个完整的异步操作就完成了,再如nodejs异步读取文件,js只需要发送一个读取文件的请求,然后nodejs会将读取文件的请求提交给他的宿主,nodejs中依赖libuv去执行I/O,然后js继续执行主线程的代码,等到I/O完成,则通知监听的代码。

所以,js异步的实现其实很简单,js本身的功能并不强大,他只是借助了他的宿主环境去执行他的指令,宿主根本上也是多线程去执行的,只是对于js语言的机制来说,他是单线程。
上面说到,异步队列池中事件的执行,只有等主线程执行完毕以后才会执行,举个例子,setTimeout是异步操作。

// 将一个异步操作提交到异步队列:
setTimeout(function() {
console.log('异步被执行了')
}, 1000)

上面代码定义了一个异步事件,将在1s后打印一个log。但是假如我们主线程中有代码需要执行超过1s,那log会不会按时打印呢?

var start = new Date();
setTimeout(function() {
console.log('异步被执行了')
}, 1000)
// 在主线程中放置一个空循环,使他在5s内处于忙碌中。
while (Date.now() - start < 5000) {
// do nothing
}

执行上面的代码,可以看到,log在5s后才被打印,也就是setTimeout并不一定准时执行,只是我们在一般的项目中,不会遇到主线程被阻塞很长时间的情况,感觉不到setTimeout的延迟。这就是上面说到的,异步队列池中的事件,需要等到主线程执行完毕才执行,也就是同步任务会阻塞接下来的代码执行。所以我们平时在编码的过程中,遇到大数据量的处理,或者遇到需要很长时间才能执行完毕的操作,尽量使用异步操作。