js异步从入门到放弃(三)- 异步任务队列(task queues)

前言

本文是对于异步系列第一篇里提到的evenloop模型中,所提到的任务队列(task queues)的展开分析

正文

说明:以下代码均使用chrome浏览器运行 关于浏览器表现的差异在最后做补充。

引子-奇怪的执行顺序

先看一个典型的例子:

console.log('script start')
// 第一个异步任务
setTimeout(()=>{
    console.log('setTimeout')
},0)

// 第二个异步任务
Promise.resolve().then(()=>{
    console.log('promise1')
}).then(()=>{ 
  console.log('promise2');
})
console.log('script end')
// 实际输出结果: 
// script start
// script end
// promise1
// promise2
// setTimeout

根据之前说过的evenloop模型,首先输出script startscript end没有什么问题;
但是接下来却发现:
先执行了Promise指定的callback而不是setTimeoutcallback-- Why?

两种任务队列(microtask queue&macrotask queue)

在之前讨论evenloop模型时,粗略提到了任务队列有2种类型:microtask queuemacrotask queue,他们的区别在于:

  • macrotask的执行:是在evenloop的每次循环过程,取出macrotask queue中可执行的第一个(注意不一定是第一个,因为我们说过例如setTimeout可以指定任务被执行的最少延迟时间,当前macrotask queue的首位保存的任务可能还没有到执行时间,所以queue只是代表callback插入的顺序,不代表执行时也要按照这个顺序)。
  • microtask的执行:在evenloop的每次循环过程之后,如果当前的执行栈(call stack)为空,那么执行microtask queue中所有可执行的任务

(某些文献内容中 直接把macrotask称为task,或者某些中文文章中把它们翻译成"微任务"和"宏任务",含义都是相似的:macrotask或者task代表相对单独占据evenloop过程一次循环的任务,而microtask有可能在一次循环中执行多个)

现在回头来解析前面的例子:

  1. 第一次执行主函数,输出script start
  2. 遇到setTimeout,将对应的callback插入macrotask queue
  3. 遇到promise,将对应的callback插入microtask queue
  4. 输出script end,主函数运行结束,执行栈清空,此时开始检查microtask queue,发现里面有可运行的任务,因此按顺序输出promise1promise2
  5. microtask queue执行完,开始新一轮循环,从macrotask queue取出setTimeout任务并执行,输出setTimeout
  6. 结束,呈现上面的输出结果。

常见异步操作对应的回调函数任务类型如下:

  • macrotask: setTimeout, setInterval, setImmediate, requestAnimationFrame, I/O, UI rendering
  • microtask: process.nextTick, Promises, Object.observe, MutationObserver

大概可以这样区分:和html交互密切相关的异步操作,一般是macrotasks;由emcascript的相关接口返回的异步操作,一般是microtasks

如何判断执行顺序

接下来看一个更复杂的例子,帮助理解不同异步任务的执行顺序




    
outer
inner

你可能感兴趣的:(javascript,异步任务队列)