前几天在理解node的事件环机制中引起了我对浏览器里Event Loop的好奇。咱们都知道javascript是单线程的,任务是须要一个一个按顺序执行的,若是javascript有两个线程,一个为DOM增长样式,一个却要删除DOM,这样岂不是就会很混乱。单线程能够节约内存,可是必须等待前一个任务完成后才能执行下一个任务。接下来理解浏览器中的Event Loop,先看一张图:javascript
heap(堆)是用户主动请求而划分出来的内存区域,好比你new Object(),就是将一个对象存入堆中,能够理解为heap存对象。
stack(栈)是因为函数运行而临时占用的内存区域,函数都存放在栈里。html
在上一张图中:
一、全部同步任务都在主线程上执行,造成一个执行栈;
二、只要异步任务有了运行结果,就在任务队列(task queue)(队列是一个先进先出的数据结构,而栈是一个先进后出的数据结构)之中放置一个事件;
三、一旦执行栈中的全部同步任务执行完毕,系统就会读取任务队列,又将队列中的事件放到stack中依次执行,就是执行异步任务中的回调函数。这个过程是循环不断的,这就是Event Loop(事件循环);vue
在上张图中,咱们看到还有宏任务(MacroTask)和微任务(MicroTask)之分。
宏任务(MacroTask)
setTimeout setInterval
微任务(MicroTask)
Promise.then MessageChannel微任务(vue中nextTick实现原理)
同步任务先执行,遇到微任务 就将微任务放入执行栈 微任务会先执行,再执行宏任务,java
先看一下图(我的理解)node
console.log(1); setTimeout(function(){ console.log(2); new Promise(function(resolve,reject){ console.log('promise'); resolve(); }).then(res=>{ console.log('promise.then'); }) }); setTimeout(function(){ console.log(4); }) console.log(5);
将这行代码放入浏览器控制台中promise
分析一下:
执行栈中同步任务先执行,先走console.log(1)和console.log(5);
接着是遇到setTimeout将它们的回调函数放入MacroTask(宏任务队列);
而后将任务队列中的回调函数依次放入主执行栈中执行,console.log(2),接着promise是当即执行,promise.then是微任务放入MicroTask中先执行;
最后执行第二个setTimeout的回调函数console.log(4);浏览器
浏览器中的Event Loop和node的Event Loop有所不一样,先来看看区别:数据结构
在node环境里,执行栈会先执行完当前任务队列,也就是两个setTimeout中的回调函数执行完才会去执行咱们的微任务队列,也就是promise.then是最后执行的,是否是很奇怪。异步
请看下面的示意图(做者 @BusyRich)。函数
这里须要注意一下,node新加了一个微任务( process.nextTick)和一个宏任务( setImmediate)
简单的来讲,就是node在处理一个执行队列的时候无论怎样都会先执行完当前队列,而后再清空微任务队列,再去执行下一个队列。
废话很少说,直接上图(我的理解)。
这里应该都明白了吧,最后注意一下,微任务中process.nextTick比promise.then快
水平不足,欢迎各位指正。