步遥——事件循环队列
js由于处理浏览器事件的原因,所以它是一个单线程的处理机制。但是为了提高cpu的利用率,js在主线程之外,会把一些计算的任务分配给子任务。事件循环就是js处理多任务同时进行的一种机制。
js维护着一个调用栈,一个消息队列,一个微任务队列 ,其中调用栈中是同步任务代码,当执行过程中,遇到了同步任务,就压入调用栈中,并立即执行该任务,执行完毕之后,就把该执行环境销毁。当遇到一个异步任务,会根据是宏任务还是微任务,将该任务中的回调压入到对应的队列中。比如setTimeout,setInterval,setImmediate,I/O,UI rendering,等都是宏任务,遇到这些任务,会将这些方法中的回调中的处理任务(回调函数内部的代码)压入到消息队列中,而如果遇到了promise,process.nextTick,这样的微任务,就会把这些函数的回调函数放入到微任务队列中(new promise中的参数是同步任务,只有.then中的才是异步任务)。
当调用栈中的代码执行完毕之后,就会先去检查微任务队列,如果微任务队列中有任务,就将微任务队列中待执行的全部任务依次压入调用栈中执行,直到微任务队列中的代码被执行完毕,然后去检查宏任务队列,再将宏任务队列中的第一个任务压入到调用栈中进行执行,执行完第一个宏任务之后,再去检查调用栈中是否有未执行的代码,优先执行调用栈中的代码,执行完毕之后再优先检查微任务队列,依次执行完所有的微任务队列中的代码后再去检查宏任务队列,还是只取出宏任务队列中的第一个任务执行,执行完毕之后,再去检查执行栈中是否有待执行任务,然后是微任务队列,最后再是宏任务队列,依次无限循环下去。。。
举个例子:
var p = new Promise(resolve => {
console.log(4);//同步先执行
resolve(5)
})
function fun1(){
console.log(1)
}
function fun2(){
setTimeout(()=>{
console.log(2)
});
fun1();
console.log(3);
p.then(resolved=>{
console.log(resolved)
}).then(()=>{
console.log(6)
})
}
setTimeout(() => {
console.log(7)//宏任务先压入宏任务队列
}, 0);
fun2()//执行func2时又压入一个宏任务和2个微任务
fun1()//同步任务,要优于以上两个宏任务和微任务,但是2个微任务的执行优先级高于2个宏任务
输出:
4
1
3
1
5
6
7
2
这就是js中的事件循环机制。了解了该循环处理事件的机制,就能很好的遇见代码的执行顺序,并再出现问题时根据执行的顺序定位对应的问题点。