单线程的 JavaScript 是怎么实现异步的?
前言
众所周知,JavaScript
是单线程的,但是不可避免的,JavaScript
也需要进行一些异步任务,比如下面这个例子
上面这个例子,执行结果会首先全部输出first
,然后全部输出second
,而不是交替执行在这个过程中,很明显发生了并发的异步任务,那么问题来了,单线程的JavaScript
是怎么实现异步的?
JavaScript
为什么是单线程的?
作为一个Java
程序员,在知道了JavaScript
是单线程之后,首先的反应就是不解,为什么一种语言会被设计成单线程的呢? 它为什么不能使用多线程来提高效率呢?
JavaScript
的单线程,与它的用途有关。作为浏览器脚本语言,JavaScript
的主要用途是与用户互动,以及操作DOM
。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定JavaScript
同时有两个线程,一个线程在某个DOM
节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?
所以,为了避免复杂性,从一诞生,JavaScript
就是单线程,这已经成了这门语言的核心特征,将来也不会改变。
JavaScript
怎么实现异步?
现在我们已经了解了JavaScript
为什么是单线程的了,那么它又是怎么实现异步的呢?JavaScript
的异步能力主要是由运行环境提供的
JavaScript
的运行环境
JavaScript Runtime
也就是JavaScript
代码运行的地方。比如JavaScript
可以在 chrome
中执行,也可以在node
中执行,chrome
与node
都是JavaScript Runtime
由上图可知,JavaScript Runtime
主要包括Js Engine
与WebAPI
等内容Js Engine
将我们编写的 JavaScript
转换为更高效的机器码,以实现更好的性能。chrome
浏览器中的 JavaScript
由 V8
引擎处理。V8
引擎主要包括内存堆与执行栈两个部分
内存堆:用于分配
JavaScript
程序使用的内存。执行栈:在执行栈中,您的
JS
代码被读取并逐行执行。
除了引擎,JavaScript Runtime
也提供了WebAPI
供JS
代码调用,WebAPI
提供了网络请求,定时器,事件监听等多种能力因为JS Runtime
并不是单线程的,而是持有一个线程池,因此WebAPI
中的代码是运行在其他线程的,自然也就提供了异步的能力
事件循环机制
JS
分为同步任务和异步任务,同步任务都在主线程上执行,形成一个执行栈栈中的代码调用
WebAPI
时也就异步任务,异步任务执行完成后,它们会在事件队列中添加各种事件而栈中的代码执行完毕,就会读取事件队列中的事件,去执行那些回调
执行栈与任务队列如此循环,也就是事件循环机制
需要注意的是,一旦执行栈中的所有同步任务执行完毕(此时JS
引擎空闲),系统就会读取任务队列,将可运行的异步任务添加到可执行栈中因此setTimeout
设置的时间并不是准确的,可能在它推入到事件列表时,主线程还不空闲,正在执行其它代码,因此存在误差。
总结
JavaScript
本质上是运行在浏览器里的脚本语言,为了简单与避免操作DOM
时引入同步问题,所以JavaScript
被设计成了单线程的语言。JavaScript
的异步能力是由运行环境提供的,通过WebAPI
与事件循环机制,单线程的JS
也可以执行异步任务。
最后
如果你觉得此文对你有一丁点帮助,点个赞。或者可以加入我的开发交流群:1025263163 相互学习,我们会有专业的技术答疑解惑
如果你觉得这篇文章对你有点用的话,麻烦请给我们的开源项目点点 star:http://github.crmeb.net/u/defu不胜感激 !
完整源码下载地址:https://market.cloud.tencent.com/products/33396
PHP 学习手册:https://doc.crmeb.com
技术交流论坛:https://q.crmeb.com
评论