微任务?区区队列罢了。

218 阅读2分钟

这是我参与8月更文挑战的第18天,活动详情查看:     8月更文挑战 ​

序言

最近在学 Vue 源码 $nextTick的时候,对任务调度有了新的认识,其中有运用到 promise 来创建微任务这个操作,所以今天我们把微任务相关的知识点总结一下,希望能对大家有启发。

什么是微任务

异步任务需要适当的管理,因此 ECMA 规定了一个内部队列PromiseJob被我们称为微任务队列。在规范中有以下描述。

  1. 队列(queue)是先进先出的:首先进入队列的任务会首先运行。
  2. 只有在 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。 ​

  1. 错误自我捕获
let promise = Promise.reject(new Error("Promise Failed!"));
promise.catch(err => alert('caught'));
​
// 不会运行:error 已经被处理
window.addEventListener('unhandledrejection', event => alert(event.reason));

错误被promise.catch捕获了,unhandledrejection 事件监听不会触发。 ​

  1. 错误没有捕获
let promise = Promise.reject(new Error("Promise Failed!"));
​
// Promise Failed!
window.addEventListener('unhandledrejection', event => alert(event.reason));

错误没有被捕获了,unhandledrejection 事件监听触发。 ​

  1. 把错误捕获放进宏任务
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 的执行流程为,执行一个宏任务,看有没有微任务,有就执行完,然后再执行下一个宏任务。不管是宏任务微任务都有自己的层级关系。