Web Workers
为了使浏览器这执行js代码时,不阻塞进程,所以HTML5制定了Web Workers标准,允许js创建多个线程,但子线程收到主线程的控制,并且不能对DOM进行操作,以防止多个线程同时对一处DOM进行不同操作,这样可以避免“一核有难,七核围观”。
为什么JS需要异步
其根本原因在于计算机对不同硬件访问速度的差异,其速度快到慢依次排序为:寄存器、L1高速缓存、L2高速缓存、L3高速缓存、内存、硬盘、Web服务器
JS如何实现异步
负责解释和执行JavaScript代码的线程只有一个,但浏览器内部还有其他线程专门负责异步任务。例如:定时器、UI、事件、网络之类专门的线程来负责相关任务处理。
JavaScript主线程会维护一个执行栈(先进后出),然后逐行执行。当遇到了异步代码(例如Promise.then、AJAX、setTimeout等),那主线程会将这部分代码交给上述的专门的线程去执行,然后跳过这些代码继续向下执行,知道当前代码段结束。
与此同时,浏览器的其他线程处理好了主线程交给其的异步代码执行前的准备工作的时候(例如AJAX数据,鼠标事件触发,定时器的时间到了等),浏览器会将当前的异步代码放入一个异步任务队列(先进先出)
每当主线程执行完当前代码,都会检查异步队列是否有任务需要执行,如果有的话,就将那个任务逐行执行,当在里面遇到了异步代码,就再交给浏览器的其他线程去处理,然后跳过这些代码继续执行,知道当前代码段结束,然后再检查异步队列,如此循环往复,所以这个过程被称为事件循环。
异步队列中的微任务和宏任务
异步队列并非只有只有一个,其主要可被分为微任务队列与宏任务队列
微任务队列:process.nextTick、Promise、Object.observe、MutationObserver
宏任务队列:setTimeout、setInterval、setMMediate、I/O、UI渲染、<script>中的js代码
两个队列都是依次执行,但是有一个很重要的区别:
当前的微任务队列中的所有任务都会被全部执行完毕,但宏队列中的任务,则只会执行最先的任务,然后就跳到了微任务队列中。
任务的优先级大体是(此顺序为《深入浅出Javascript》中所提出的):
idle观察者 > I/O观察者 > check观察者
process.nextTick(idle) > Promise(原生实现) setTimeout(I/O) > setImmediate (check)
setImmediate(function(){
console.log(1)
},0)
setTimeout(function(){
console.log(2)
},0)
new Promise(function(resolve){
console.log(3)
resolve()
console.log(4)
}).then(function(){
console.log(5)
})
console.log(6)
process.nextTick(function(){
console.log(7)
})
console.log(8)
执行结果是
3
4
6
8
7
5
2
1