这是我参与8月更文挑战的第18天,活动详情查看: 8月更文挑战
序言
最近在学 Vue 源码 $nextTick的时候,对任务调度有了新的认识,其中有运用到 promise 来创建微任务这个操作,所以今天我们把微任务相关的知识点总结一下,希望能对大家有启发。
什么是微任务
异步任务需要适当的管理,因此 ECMA 规定了一个内部队列PromiseJob被我们称为微任务队列。在规范中有以下描述。
- 队列(queue)是先进先出的:首先进入队列的任务会首先运行。
- 只有在 JavaScript 引擎中没有其它任务在运行时,才开始执行任务队列中的任务。
或者,简单地说,当一个 promise 准备就绪时,它的 .then/catch/finally 处理程序(handler)就会被放入队列中:但是它们不会立即被执行。当 JavaScript 引擎执行完当前的代码,它会从队列中获取任务并执行它。
例子
let promise = Promise.resolve();
promise.then(() => alert("coolFish ready!"));
alert("code finished"); // 这个会显示
如上例所示,alert("code finished")明明在alert("coolFish ready!")后面却会先执行,这是因为alert("coolFish ready!")放入了微任务队列,当JS执行完当前代码时,才会执行他。
控制执行顺序:
Promise.resolve()
.then(() => alert("coolFish ready!"))
.then(() => alert("code finished"));
未处理的 rejection
如果一个 promise 的 error 未被在微任务队列的末尾进行处理,则会出现 未处理的 rejection。
- 错误自我捕获
let promise = Promise.reject(new Error("Promise Failed!"));
promise.catch(err => alert('caught'));
// 不会运行:error 已经被处理
window.addEventListener('unhandledrejection', event => alert(event.reason));
错误被promise.catch捕获了,unhandledrejection 事件监听不会触发。
- 错误没有捕获
let promise = Promise.reject(new Error("Promise Failed!"));
// Promise Failed!
window.addEventListener('unhandledrejection', event => alert(event.reason));
错误没有被捕获了,unhandledrejection 事件监听触发。
- 把错误捕获放进宏任务
let promise = Promise.reject(new Error("Promise Failed!"));
setTimeout(() => promise.catch(err => alert('caught')), 1000);
// Error: Promise Failed!
window.addEventListener('unhandledrejection', event => alert(event.reason))
我们发现错误依旧被 unhandledrejection 事件监听触发。因为错误捕获是在微任务队列清空以后执行的,这里的错误捕获,被setTimeout 放进了宏任务。所以微任务执行完毕,还没有catch捕获,就被unhandledrejection 监听触发。 宏任务与微任务案例
setTimeout(() => {
//执行后 回调一个宏事件
console.log('内层宏事件3')
}, 0)
console.log('最外层宏事件1');
new Promise((resolve) => {
console.log('最外层宏事件2');
resolve()
}).then(() => {
console.log('微事件1');
}).then(()=>{
console.log('微事件2')
})
结果如下
最外层宏事件1
最外层宏事件1
微事件1
微事件2
内层宏事件3
JS 的执行流程为,执行一个宏任务,看有没有微任务,有就执行完,然后再执行下一个宏任务。不管是宏任务微任务都有自己的层级关系。