一、event loop (事件循环)
- JS是单线程运行的
- 异步要基于回调来实现
- event loop就是异步回调的实现原理
JS执行顺序?
从前到后,一行行执行,如果某行执行报错,停止下面代码的执行,先同步后异步
Event loop过程
Call Stack、Web APIs、CallBack Queue(回调队列)、Event Loop
//1.同步任务先执行,放到Call Stack 中,先打印
console.log(1)
//2.下一行,遇到异步任务,先放到Web APIs中,等待
setTimeout(function fn() {
console.log(3)
});
//3.同步任务,继续执行完成,开始运行event loop, 2操作中的方法时间一到,先放到callBack Queue,等待event loop轮询,找到就放Call Stack执行
console.log(2)
- 同步代码,一行一行放在 Call Stack执行。
- 遇到异步,会先‘纪录’下(放在web APIs)待时机(定时、网络请求等)
- 时机到了,就移动到Callback Queue
- 如果Call Stack为空(即同步代码执行完成)Event Loop开始工作,轮询查找 callBack Queue
- 如果则移动到Call Stack执行
- 然后继续轮询查找(永动机一样)
DOM事件和event loop的关系:
JS是单线程的,异步(SetTimeout,ajax等)使用回调,基于event loop,DOM事件也使用了回调,基于event loop(操作事件时触发调用即可)
DOM渲染和event loop的关系:
JS单线程的,而且和DOM渲染公用一个线程,需要留时机给DOM渲染
每次Call Stack清空(即每次轮询结束),即同步任务执行完,都是DOM重新渲染的机会,DOM结构如有改变则重新渲染,然后再去触发下一次Event loop
二、宏任务(macroTask)和微任务(microTask)
- 宏任务:setTimeout,setInterval,ajax,DOM事件(DOM渲染后触发)
- 微任务:Promise.then async/await(DOM渲染前触发)
- 微任务执行时机要比宏任务要早
过程和区别
- 微任务:进入Micro task Queue(微任务回调队列)
- 宏任务:进入CallBack Queue(回调队列)
- 区别:微任务是ES6语法规定的,宏任务是由浏览器规定的
三、最终总结一下我自己的解题思路
- 主流程 :先查找异步任务先放到异步队列,再从头走同步任务,遇到异步任务都把任务放到异步队列,等待执行。
- 同步任务(Call Stack执行),遇到微任务按顺序先放到Micro task Queue(微任务回调队列),等到执行。
- 同步任务执行完成,先执行Micro task Queue的微任务,微任务执行完后才能进行下一步。
- 最后一步,Event Loop会去获取,异步队列中的任务,继续循环进行(Call Stack执行)。
几个注意的点:
- Promise 默认返回值为resolve()
new Promise((resolve)=> {
console.log(6);
resolve();
}).then(()=>{
console.log(7)
}).then(()=>{
console.log(8)
//return Promise.reject("异常") 存在这句话才会打印出来9,否则默认执行成功,结果6 7 8
}).catch(()=>{
console.log(9)
})
- async/await(注意:同步运行async方法先跳过,调用在执行)
async function async1() {
await async2()
console.log(2)
}
async function async2() {
console.log(3)
}
async1()
//上述代码等价于
new Promise(resolve => {
console.log(3)
resolve()
}).then(res=> {
console.log(2)
})
简单例子
console.log(1)//2.同步 先打印
async function async1() {//3.anync 方法 先跳出
await async2()//6.先执行async2,后续代码等待(Promise.then为微任务)
console.log(2)//8.微任务1排序,继续主流程
setTimeout(function() {//9.异步任务2放到异步队列
console.log(9)
},0)
}
async function async2() {//4.anync 方法 先跳出
console.log(3)//7.先打印,回到async1(),微任务1排序
}
async1()//5.anync方法调用,回到async1()
setTimeout(function() { //9.异步任务1先到异步队列
new Promise(function(resolve) {
console.log(4);
resolve();
}).then(function() {
console.log(5)
})
}, 0)
new Promise(function(resolve) {
console.log(6);//10.Promise示例同步任务先打印
resolve();
}).then(function() {
console.log(7) //11.微任务2排序
}).then(function() {
console.log(8)//13.微任务3排序
})
//结果:同步任务==》微任务==》异步任务(1,3,6,2,7,8,4,5,9)
//同步任务(1,3,6)
//微任务(2,7,8)
//异步任务(4,5,9)