JavaScript是一门单线程语言,即当有一个任务在执行的时候,其余任务须要在后面等待。但在实际场景中,有些任务能够放在比较靠后的位置,好比加载网络资源时发送的异步请求。网络
事件循环就是JavaScript异步执行机制的一种实现方式。异步
前面说到执行上下文存在建立阶段和执行阶段,这一章节主要针对执行阶段来说。即在分析JavaScript代码执行时,首先要作一次执行上下文建立阶段的分析,然后才能利用这篇文章的知识点来分析JavaScript的执行机制。函数
咱们常说JavaScript是按照代码顺序一行行执行下来的,但这段代码实际输出的顺序是2,1。这是为何呢?post
setTimeout(function(){ console.log(1); },0) console.log(2);
JavaScript引擎会智能地将任务分红同步任务和异步任务。spa
首先JavaScript引擎会将总体script代码放入执行栈中,遇到同步任务则直接进入主线程执行,遇到异步任务时注册回调函数,并将异步任务放进事件队列中等待。线程
待主线程中的全部同步任务执行完毕后,才会去事件队列处取任务。根据队列先进先出的特色,最早进入事件队列的异步任务会被率先执行。3d
而在实际场景中,并非从事件队列一一取出异步任务直接执行这么简单。从宏观定义上,咱们将JavaScript的任务分为同步任务和异步任务,但在事件循环中,更加精细的分法是将任务分为宏任务和微任务。不一样类型的任务会进入不一样的事件队列中。code
在JavaScript引擎执行代码的过程当中,首先将总体代码做为一个宏任务执行,遇到通常的同步代码当即执行,遇到定时器相关如setTimeout这种宏任务代码则先将其放入到宏任务事件队列中,遇到Promise这种则将其catch、then函数放入到微任务队列中。blog
总体代码做为第一次宏任务执行完毕后,JavaScript引擎会先到微任务队列中看看有没有任务,若有任务则将其所有执行,若是没有则进入到下一次宏任务中。也就是说JavaScript执行机制是按照宏任务-微任务-宏任务-微任务...这样子循环执行的。队列
根据队列先进先出的特色,取出宏任务事件队列中第一个宏任务率先执行,遇到同步代码当即执行,遇到微任务则继续进入微任务事件队列。待这一次的宏任务执行完毕后,则继续执行全部的微任务。如此循环下去,则就是事件循环。
先举个简单的例子
setTimeout(function(){ console.log(0); },3000); new Promise((resolve,reject)=>{ console.log(2); resolve(); }).then(()=>{ console.log('resolve'); } ); console.log(1)
第一遍事件循环过程:
第二遍事件循环过程:
上面给定时器的延迟时间是3秒钟,有无多是时间片竞争的机制抢着输出?试着把setTimeout的延迟时间改成0。
setTimeout(function(){ console.log(0); },0); new Promise((resolve,reject)=>{ console.log(2); resolve(); }).then(()=>{ console.log('resolve'); } ); console.log(1)
发现输出的顺序一致,实际上将延迟设置为0,也不是当即执行。
依据上面的结论,将setTimeout的延迟时间设为0的意思是待主线程空闲后即可以执行,还得等主线程按照宏任务-微任务-宏任务-微任务的事件循环顺序执行完毕后。实际上即便主线程空闲了,延迟执行也不会达到0秒当即执行,最低也是4ms。
setTimeout
、setInterval
Promise
、nextTick
参考文章: https://juejin.im/post/59e85e...