写在前面
事件循环即EventLoop,说到EventLoop就必须先了解几个概念:单线程、stack、queue、webapi、Macrotask、Microtask。
单线程
众所周知,js是单线程语言,即同一个时间只能做一件事,如果处理一件事情需要很久的话,后面的事情就会被阻塞。
为什么js是单线程语言呢?
这与它的用途有关。作为浏览器脚本语言,JavaScript的主要用途是与用户互动,以及操作DOM。这决定了它只能是单线程,否则会带来很复杂的同步问题。比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?
相关概念
- stack(执行栈):执行js代码的地方,先进后出。
- queue(回调队列):异步任务中可执行代码的等待区域,先进先出。
- webapis: 维护异步任务的地方,直接点说就是主线程之外的其他线程,这种说法可能不够准确。
- Microtask(微任务):回调队列的一种,执行栈空出来的时候优先取Microtask队列里的代码执行。
- Macrotask(宏任务):回调队列的一种,优先级低于Microtask。
EventLoop 具体流程
举一个例子具体说下EventLoop的流程:
console.log(1);setTimeout(function(){ console.log(2)},0);console.log(3)//输出结果是1,3,2复制代码
- console.log(1)被压入执行栈。
- setTimeout在执行栈被识别为异步任务,放入webapis中。
- console.log(3)被压入执行栈,此时setTimeout的可执行代码还在回调队列里等待。
- console.log(3)执行完成后,从回调队列头部取出console.log(2),放入执行栈。
- console.log(2)执行。
再举一个例子说下Microtask和Macrotask:
console.log(1);setTimeout(()=>{ console.log(2)})var p = new Promise((resolve,reject)=>{ console.log(3) resolve("成功")})p.then(()=>{ console.log(4)})console.log(5)//按照上个例子的理解输出结果应该为:1、3、5、2、4,但实际上输出的结果为:1、3、5、4、2复制代码
总结
- 第二个例子的输出结果为什么是:1、3、5、4、2呢?前面Microtask概念里提到过执行栈空出来的时候优先取Microtask队列里的代码执行。难道Promise的then是微任务!? 是的,then是微任务。
- 常见的微任务有哪些:process.nextTick、Promise、Object.observe、MutationObserver,优先级也是按照这个顺序。
- 常见的宏任务:setTimeout/setInterval、事件、ajax等。
- 盗张图来看下EventLoop: