JS中的EventLoop
一、宏任务和微任务
**宏任务(macrotask):**主代码块(整体代码)、setTimeout、setInterval、 setImmediate 等 。
微任务( microtask ): Promise.then、 process.nextTick 等。
二、 EventLoop 的执行流程
- 一开始将整体代码作为一个宏任务执行。
- 从上到下的执行过程中,同步代码直接执行,宏任务代码开放入宏任务队列,微任务代码放入微任务队列。
- 当前的宏任务执行完后,检查微任务队列,没有的话直接执行下一步,如果有则依次执行微任务队列。其中执行的微任务里包含宏任务或微任务时,同(2)的操作,直到微任务队列为空。
- 查看宏任务队列里还有没有任务,有的话拿出来执行。
- 新拿到的宏任务,回到第(2)步,依次执行,直到 宏任务和微任务队列都为空 。
三、案例
1、简单的带解释的哈
Promise.resolve().then(()=>{
console.log('Promise1')
setTimeout(()=>{
console.log('setTimeout2')
},0)
});
setTimeout(()=>{
console.log('setTimeout1')
Promise.resolve().then(()=>{
console.log('Promise2')
})
},0);
console.log('start');
过程:
- 将整体代码作为第一个宏任务,从下到上执行
- 遇到
Promise.resolve().then
,将其整个代码块放入微任务队列 - 遇到
setTimeout
,将其整个代码块放入宏任务队列 - 遇到
console.log('start')
,直接执行,输出 start - 第一个宏任务执行完毕,检查微任务队列,发现里面有
Promise.resolve().then
代码块,执行该代码块。 - 遇到
console.log('Promise1')
,直接执行,输出 Promise1 - 遇到
setTimeout
,将其整个代码块放入宏任务队列 - 该微任务执行完后,发现此时微任务队列里已经没有任务了
- 去检查宏任务队列,发现里面有两个
setTimeout
任务,将最早的setTimeout
取出执行(第2步的那个) - 遇到
console.log('setTimeout1')
,直接执行,输出 setTimeout1 - 遇到
Promise.resolve().then
,将其整个代码块放入微任务队列 - 此时的宏任务执行完了,检查微任务队列,发现里面有任务,是
Promise.resolve().then
(第11步那个),执行微任务队列 - 遇到
console.log('Promise2')
,直接执行,输出 Promise2 - 该微任务执行完后,发现此时微任务队列里又双叒叕没有任务了,(那就接着来繁琐的循环吧)
- 去检查宏任务队列,发现里面还有一个宝贝任务诶,取出
setTimeout
执行(这次是第7步的那个哦) - 遇到
console.log('setTimeout2')
,直接执行,输出 setTimeout2 - 这个宏任务也搞好了,再去瞅瞅微任务队列,妈耶没得了(开心)
- 在瞅瞅宏任务队列,妈耶也没得,终于搞好了,再也不用转来转去了
答案:
// start
// Promise1
// setTimeout1
// Promise2
// setTimeout2
2、async/await
async function fn1(){
console.log(1)
await fn2()
console.log(2)
}
async function fn2(){
console.log(3)
}
fn1()
new Promise(function(resolve){
console.log(4)
resolve()
}).then(()=>{
console.log(5)}
)
答案:
// 1
// 3
// 4
// 2
// 5
解释:
是不是有点和你想的不一样, 简单来说,await前面的是同步,await后面的是微任务
await fn2()
console.log(2)
// 等价于
fn2().then(() => {
console.log(2)
})
// Promise.then是微任务,这下了解吧
3、上面的复杂一丢丢版本
async function async1(){
console.log('async1 start')
await async2()
console.log('async1 end')
}
async function async2(){
console.log('async2')
}
console.log('script start')
setTimeout(function(){
console.log('setTimeout0')
},0)
setTimeout(function(){
console.log('setTimeout3')
},3)
setImmediate(() => console.log('setImmediate'));
process.nextTick(() => console.log('nextTick'));
async1();
new Promise(function(resolve){
console.log('promise1')
resolve();
console.log('promise2')
}).then(function(){
console.log('promise3')
})
console.log('script end')
答案:
script start
async1 start
async2
promise1
promise2
script end
nextTick
async1 end
promise3
setTimeout0
setImmediate
setTimeout3
**解释:**不解释了,自己慢慢瞅去,我当年也是这么过来的(难受)
4、微任务套微任务
async function a1 () {
console.log('a1 start')
await a2()
console.log('a1 end')
}
async function a2 () {
console.log('a2')
}
console.log('script start')
setTimeout(() => {
console.log('setTimeout')
}, 0)
Promise.resolve().then(() => {
console.log('promise1')
})
a1()
let promise2 = new Promise((resolve) => {
resolve('promise2.then')
console.log('promise2')
})
promise2.then((res) => {
console.log(res)
Promise.resolve().then(() => {
console.log('promise3')
})
})
console.log('script end')
答案:
script start
a1 start
a2
promise2
script end
promise1
a1 end
promise2.then
promise3
setTimeout
**解释:**你觉得这里应该有解释吗
5、关于 process.nextTick 的补充
process.nextTick(() => console.log(1));
Promise.resolve().then(() => console.log(2));
process.nextTick(() => console.log(3));
Promise.resolve().then(() => console.log(4));
答案:
1
3
2
4
不是,1,2,3,4 哦
解释:
process.nextTick
虽然是像微任务那样执行的,但是还是有不同的
- 两者不在同一个队列里,
process.nextTick
是一个独立于 eventLoop 的任务队列 process.nextTick
优先于微任务执行