什么是事件循环
浏览器是单线程的,为了保证同步和异步任务的执行顺序,有了自己一套规则。
浏览器所有的事件类型
- 同步任务:如变量的声明,函数的声明
- 异步任务:异步任务分为宏任务、微任务
- 宏任务:如setTimeout、setInterval、浏览器的I/O等
- 微任务:await的下一行、promise 的then或者catch、vue中的nextTick()等
执行顺序
优先级: 同步任务->微任务->宏任务
这是几种任务的优先级。也就是先执行主线程中的宏任务(可以理解按代码书写顺序执行),而在执行宏任务之前先执行在微任务队列中的微任务。
console.log(1) // 同步任务
const pA = new Promise((resolve, reject)=>{
resolve(2)
})
pA.then((resolve)=>{
console.log(resolve)
}) // 微任务
setTimeout(()=>{
console.log(3)
},0) // 宏任务
const pB = new Promise((resolve, reject)=>{
resolve(4)
})
pB.then((resolve)=>{
console.log(resolve)
}) // 微任务
console.log(5)
根据描述,你可以猜一下以上代码执行顺序,如果你的结果是"1,5,2,4,3"那么很好你掌握了 浏览器各个任务执行的顺序。
解释
第1行是同步任务,直接输出1。
第7、10、17行是异步任务,分别进入微任务队列和宏任务队列等待执行
第20行是同步任务,直接输出5。
同步任务执行完了,开始执行异步任务
先执行微任务7,17 输出2,4
后执行10 输出3
最终结果是"1,5,2,4,3"
练习
在开发中往往更加的复杂。比如:
async function foo(){
console.log('foo')
setTimeout(()=>{
console.log("11")
},0)
}
async function bar(){
console.log('bar start')
await foo()
console.log("bar end")
}
console.log('script start')
setTimeout(function(){
console.log('setTimeout')
},0)
bar();
new Promise(function(resolve){
console.log('promise executor')
resolve();
}).then(function(){
console.log('promise then')
})
console.log('script end')
"script start"
"bar start"
"foo"
"promise executor"
"script end"
"bar end"
"promise then"
"setTimeout"
"11"
解释
首先运行第12行同步任务,直接输出"script start",14行是宏任务,加入宏任务队列。 进入第16行进入bar(), bar()中的第8行是同步任务,直接输出"bar start";
此时微任务队列[ ], 此时宏任务队列[ 14]
到达第9行是一个异步任务并且加了await, await 后面是个异步但不是promise 对象,如果是promsie 对象需要等待resolve()获取reject(),所以执行完同步任务第3行直接跳出foo();输出"foo",并把第4行加入宏任务队列中。
此时微任务队列[ ], 此时宏任务队列[14,4]
即便是第9行不是await 一个promise 对象,但是await 会包装一个非promise对象为promise 对象并执行resolve,也就是await 后面,即第10行是个微任务,直接加入微任务队列。
此时微任务队列[10], 此时宏任务队列[14,4]
继续在Promise的声明里面,所以是个同步任务,输出"promise executor";
此时微任务队列[10], 此时宏任务队列[14,4]
21 行为then里面是微任务即21行是微任务,加入微任务队列,进入23行同步任务执行输出"script end"
此时微任务队列[10,21], 此时宏任务队列[14,4]
到这里所有的同步任务执行完成,进入微任务队列,第一微任务是第10行输出"bar end"
此时微任务队列[21], 此时宏任务队列[14,4]
继续执行队列中的微任务21行输出'promise then'
此时微任务队列[ ], 此时宏任务队列[14,4]
到这里微任务队列里面没有微任务了,开始执行宏任务队列,此时14行输出"setTimeout"
此时微任务队列[ ], 此时宏任务队列[4]
再执行第二个宏任务,输出等4行"11"。
此时微任务队列[ ], 此时宏任务队列[ ]