Table of contents
什么是 EventLoop (事件轮询)
事件轮询就是解决 JavaScript 单线程处理异步操作时的一些缺陷。让 JavaScript 做到既是单线程,又不会阻塞的核心机制。是用来协调各种事件、用户交互、脚本执行、UI 渲染、网络请求等的一种机制。
宏任务和微任务优先问题
在任务队列中注册的两种异步任务分为 宏任务、微任务。可以认为任务队列中只有一个微任务队列,但是可以有多个宏任务队列
- 宏任务(macro Task)
- setTimeout / setInterval
- script(整体代码)
- setImmediate(Node环境)
- UI 渲染
- requestAnimationFrame
- ...
- 微任务
- Promise的then()、catch()、finally()里面的回调
- process.nextTick(Node 环境)
- ...
EventLoop 的执行顺序
按照我的理解,若不看第一个宏任务(script)
,从同步代码执行结束后开始。微任务会优先执行,等到所有的微任务执行完成,才会执行宏任务队列。但是要注意是否在同一级。
代码示例
例一
console.log('script start');
setTimeout(() => {
console.log('北歌');
}, 1 * 2000);
Promise.resolve()
.then(function() {
console.log('promise1');
})
.then(function() {
console.log('promise2');
});
async function foo() {
await bar()
console.log('async1 end')
}
foo()
function bar() {
console.log('async2 end')
}
async function errorFunc () {
try {
await Promise.reject('error!!!')
} catch(e) {
console.log(e)
}
console.log('async1');
return Promise.resolve('async1 success')
}
errorFunc().then(res => console.log(res))
console.log('script end');
可以先试着写一下答案是什么
代码从上开始执行,先执行一个宏任务(script)
console.log('script start'); // 同步任务 宏0
setTimeout(() => {
console.log('北歌'); // 宏任务 宏1-1
}, 1 * 2000);
Promise.resolve()
.then(function() {
console.log('promise1');// 微任务 微1-1
})
.then(function() {
console.log('promise2'); // 微2-1 微任务,属于微任务产生的微任务 得等到微1执行完,并且resolve了,才会注册这个微任务
});
async function foo() {
await bar() // promise的语法糖,也是同步任务
console.log('async1 end') // 微任务,微1-2
}
foo()
function bar() {
console.log('async2 end') // 同步任务 宏0
}
async function errorFunc () {
try {
await Promise.reject('error!!!')
} catch(e) {
console.log(e)// 微任务,微1-3
}
console.log('async1'); // 微任务,微1-4
return Promise.resolve('async1 success') // 微2-2 微任务,属于微任务产生的微任务,需要一次执行完
}
errorFunc().then(res => console.log(res))
console.log('script end'); // 同步任务 宏0
根据上述描述,执行的顺序是
script start => async2 end => script end => promise1 => async1 end => error!!! =>
async1 => promise2 => async1 success => 北歌
例二
console.log('1'); // 同步代码 宏0
setTimeout(() => {
console.log('2'); // 宏1-1
Promise.resolve().then(() => {
console.log('3'); // 微任务,属宏任务产生的微任务,微1-3
})
new Promise((resolve) => {
console.log('4'); // 宏1-2
resolve();
}).then(() => {
console.log('5') // 微任务,属宏任务产生的微任务,微1-4
})
})
Promise.reject().then(() => {
console.log('13'); // 不执行
}, () => {
console.log('14'); // 微任务,微1-1
})
new Promise((resolve) => {
console.log('7'); // 同步任务 宏0
resolve();
}).then(() => {
console.log('8') // 微任务 微1-2
})
setTimeout(() => {
console.log('9');// 宏1-3
Promise.resolve().then(() => {
console.log('10');// 微任务,属宏任务产生的微任务,微1-5
})
new Promise((resolve) => {
console.log('11');// 宏1-4
resolve();
}).then(() => {
console.log('12')// 微任务,属宏任务产生的微任务,微1-6
})
})
执行的顺序是
1 -> 7 -> 14 -> 8 -> 2 -> 4 -> 3 -> 5 -> 9 -> 11 -> 10 -> 12
例三
new Promise((resolve, reject) => {
console.log(1)
resolve()
})
.then(() => {
console.log(2)
new Promise((resolve, reject) => {
console.log(3)
setTimeout(() => {
reject();
}, 3 * 1000);
resolve()
})
.then(() => {
console.log(4)
new Promise((resolve, reject) => {
console.log(5)
resolve();
})
.then(() => {
console.log(7)
})
.then(() => {
console.log(9)
})
})
.then(() => {
console.log(8)
})
})
.then(() => {
console.log(6)
})
执行的顺序是
1 -> 2 -> 3 -> 4 -> 5 -> 6 -> 7 -> 8 -> 9