前言
事件循环过程:js是单线程,执行时根据压入执行栈的顺序进行执行。首先执行同步代码,遇到任务源时,分配到相应的任务队列中(宏任务和微任务),执行完同步任务后,检查执行栈是否为空,如果为空,检查微任务队列是否为空,如果不为空,则按照先进先出的方式一次性执行完微任务队列,如果为空,执行下一个宏任务。循环往复形成事件循环。过多不再赘述,进入正题。
要点
1.每次单个宏任务执行完后都会检查微任务队列是否为空
2.每次宏任务产生的微任务队列都是新创建的,宏任务队列只有一个
示例+解析(由易到难)
示例1:
console.log(1)
new Promise((resolve, reject) => {
console.log(2)
resolve()
}).then(res => {
console.log(3)
})
console.log(4)
// 输出: 1 2 4
复制代码
解析:
-执行同步代码,输出1
-new Promise
内部属于同步输出2
-.then
推入微任务队列,之后执行代码输出4
-整个script代码块属于宏任务此时执行栈为空检查微任务队列,执行微任务输出3
示例2:
console.log('start')
setTimeout(() => {
console.log('setTimeout')
}, 0)
new Promise((resolve) => {
console.log('promise')
resolve()
}).then(() => {
console.log('then1')
}).then(() => {
console.log('then2')
})
console.log('end')
// 输出: start promise end then1 then2 setTimeout
复制代码
解析:
-执行同步代码输出'start'
-遇到setTimeout
放入宏任务队列,继续执行
-new Promise
内部属于同步,输出promise
-之后的两个.then
放入微任务队列,继续执行
-输出'end'
-执行栈为空 (同一次事件循环中,微任务永远在宏任务之前)
-检查微任务队列,输出'then1'
,'then2'
-清空微任务队列后执行下一个宏任务
-输出setTimeout
示例3(示例2变种):
console.log('start')
setTimeout(() => {
console.log('setTimeout')
new Promise((resolve) => {
console.log('promise2')
resolve()
}).then( () => {
console.log('then3')
})
}, 0)
new Promise((resolve) => {
console.log('promise1')
resolve()
}).then(() => {
console.log('then1')
}).then(() => {
console.log('then2')
})
console.log('end')
//输出 start promise1 end then1 then2 setTimeout promise2 then3
复制代码
解析:
-执行同步代码,输出'start'
-遇到setTimeout
放入宏任务队列,继续向下执行
-new Promise
内部属于同步,输出promise1
-之后的两个.then
放入微任务队列,继续执行
-输出'end'
-检查微任务队列,输出'then1'
,'then2'
-清空微任务队列后执行下一个宏任务
-输出setTimeout
-执行new Promise
内部代码,输出promise2
-.then
放入微任务队列
-执行栈为空,检查微任务队列,输出then3
(注意:此时的微任务队列是新创建)
示例4(示例3的变种):
console.log('start')
setTimeout(() => {
console.log('setTimeout1')
new Promise((resolve) => {
console.log('promise1')
resolve()
}).then( () => {
console.log('then1')
})
}, 0)
new Promise((resolve) => {
console.log('promise2')
resolve()
}).then(() => {
console.log('then2')
}).then(() => {
console.log('then3')
})
new Promise((resolve) => {
console.log('promise3')
resolve()
}).then(() => {
console.log('then4')
}).then(() => {
console.log('then5')
})
setTimeout(() => {
console.log('setTimeout2')
new Promise((resolve) => {
console.log('promise4')
resolve()
}).then( () => {
console.log('then6')
})
}, 0)
console.log('end')
// 输出: start promise2 promise3 end then2 then4 then3 then5 setTimeout1 promise1 then1 setTimeout2 promise4 then6
复制代码
解析:
-执行同步代码,输出'start'
-遇到setTimeout
放入宏任务队列,继续向下执行
-new Promise
内部属于同步,输出promise2
,继续执行
-之后的两个.then
和下一个promise的.then
交替放入微任务队列
-new Promise
内部属于同步,输出promise3
-遇到setTimeout
放入宏任务队列,继续向下执行 (注意:此处两个setTimeout被放入同一个宏任务队列)
-输出'end'
-检查微任务队列,输出then2 then4 then3 then5
-执行下一个宏任务(取出第一个setTimeout
的回调执行)
-输出setTimeout1
-执行new Promise
内部代码,输出promise1
-.then
放入微任务队列
-执行栈为空,检查微任务队列,输出then1
(新创建的微任务队列)
-执行栈为空,执行宏任务队列的下一个宏任务
-输出setTimeout2
-执行new Promise
内部代码,输出promise4
-.then
放入微任务队列
-执行栈为空,检查微任务队列,输出then6
(新创建的微任务队列)
⚠️注意:该案列中有两个Promise
,并且每个Proimise
有两个.then
。当含有多个Promise
时,他们的then操作会交替执行,这是由于推入微任务队列的时机不同产生了这种现象
总结
理解两个要点,对应着再看代码输出,一步一步分析会发现事件循环也并不是那么的难。