阅读本文,你将知道:html
在最初学习JavaScript的时候,就从各个地方得知JavaScript是一门单线程编程语言,可是使人疑惑的是,为何一个单线程语言可以同时执行HTTP请求同时渲染页面?为何代码的书写顺序和执行顺序并不一致?web
这就是异步带来的效果ajax
这里咱们必须达到以下的共识:编程
这里须要注意的是GUI渲染进程和JavaScript引擎进程是互斥的,由于若是这两个线程能够同时运行的话,JavaScript的DOM操做将会扰乱渲染线程执行渲染先后的数据一致性。浏览器
本文想要探讨的,是JavaScript线程里的异步设计,千万别和多线程混淆了。网络
想要了解更多关于浏览器多线程机制请参考:www.cnblogs.com/hksac/p/659…多线程
一切得先从CPU开始讲起:异步
CPU的指令执行速度是远高于硬盘读取速度和主存读取速度的。而I/O操做就会涉及到硬盘存取和主存读取,常见的I/O操做有文件I/O,网络I/O。(I/O = Input / Output)。编程语言
因此,观察如下这一段伪代码:函数
var a = 2;
for(let i =0;i<10;i++){
doSomeWork();
}
let buffer = openFile('./work.txt')
buffer.add('hello world');
复制代码
在CPU眼中,他会把代码当作这两部分:
绿色部分由于不涉及到I/O操做,因此CPU执行速度超快,可是当运行到红色部分时,倒是一个很是耗时的操做,而这段时间,CPU是处于一个'无所事事'的状态(DMA获取总线控制权以后一切I/O与CPU无关),由于文件若是没有读取进来,下面的工做也没法开展。
同步在这里的意思,即书写代码的顺序就是代码执行的顺序,若是JavaScript设计成同步的话,那么当执行到openFile
这一行的时候,将会等待该I/O操做完成CPU才继续往下执行。
设想一下,当发送Ajax请求(网络I/O)的时候,整个页面被阻塞没法操做将会是多差的体验。
而诸如鼠标点击事件,滑动事件,失焦事件,在CPU看来,都是处理得特别慢的事件(虽然对咱们来讲是一瞬间的事情),若是将JavaScript设计成同步,也会特别浪费CPU性能。
而阻塞和非阻塞关注的CPU在I/O发生时的工做状况
在上面这个读取文件的例子中
若是没法区分同步
和阻塞
,请参考这里
异步则解决了代码被耗时任务阻止其往下执行的缺点
多线程异步有着比较好的解决方案:
JavaScript是一门单线程语言,自己没法提供多线程,那么是经过怎样的机制来实现异步的?
先给出答案:JavaScript经过事件循环和浏览器各线程协调共同实现异步
JavaScript认为任务分为两种,一种是全由CPU决定完成速度的任务,咱们称其为同步任务,一种是由多种因素(如硬盘读取速度,网速,点击反馈速度)决定完成速度的任务,咱们称其为异步任务。
举个简单的例子
JavaScript将全部的异步任务都会放进一个队列里面,在执行完全部的同步任务以后,会去队列中找到最早进入队列的异步任务执行。
仔细观察上图,结合本文在最开始提到的浏览器多线程设计:
由于诸如事件触发,http请求都是耗时没法直接肯定的任务,也就是说JavaScript线程没法得知异步的任务回调函数究竟何时会写入异步任务队列,那么这个地方,就须要一个机制,去时刻轮询这个任务队列,这就是事件循环(event loop)
如今咱们再看以下代码的执行顺序:
var req = new XMLHttpRequest();
req.open('GET', url);
req.onload = function (){};
req.onerror = function (){};
req.send();
复制代码
真正的执行顺序是
var req = new XMLHttpRequest();
req.open('GET', url);
req.send(); // i am here
req.onload = function (){};
req.onerror = function (){};
复制代码
同理
while(1){
console.log('1')
}
setTimeOut(()=>{
console.log('00000000000000000')
},1)
复制代码
也将一样永远不会输出00000000000000000
由于JavaScript的工做环境是一个典型的异步应用场景:充斥着各类ajax事件和浏览器事件。各个事件的触发时间和获得反馈的时间都不得而知,若是设计成同步语言,将会带来极差的浏览器使用体验。 须要设计一个成一个生产者-消费者
模型(也能够看作是观察者模式),来管理这样的异步任务。
浏览器须要作的事情太多了,一手须要负责渲染,一手须要负责http请求,一手还须要执行JavaScript,将JavaScript设计成单线程不只可以让浏览器更好地控制各个线程,同时对开发者来讲也更简单。多线程涉及到锁,临界区,冲突解决的学习成本仍是比较高的。
再次来看一下这一句话:
JavaScript是异步事件驱动的单线程编程语言
异步:写代码顺序不必定是执行顺序,JavaScript线程先执行同步任务。 事件驱动:其余线程在各事件完成后将回调函数写入队列,都是以抽象事件做为触发机制的。 单线程:不能开多线程而是用eventloop来实现异步的。
JavaScript经过事件循环和浏览器各线程协调共同实现异步
JavaScript的异步设计很是优秀,这也让基于V8引擎的Node在服务端大方异彩,可以更加简单地开发出适合高密集I/O的web应用。
以后会基于JavaScript的EventLoop总结下关于Node的异步I/O(涉及到多线程)
若是喜欢,请关注
最新博客会最早更新在http://www.helloyzy.cn