美文网首页
Microtasks 与 Macrotasks

Microtasks 与 Macrotasks

作者: 任无名F | 来源:发表于2017-11-03 12:50 被阅读0次

    作为一个【实际问题驱动学习】的前端萌新,每次学习动力的激发都来自于某个问题:

    console.log('start')
    
    const interval = setInterval(() => {  
      console.log('setInterval')
    }, 0)
    
    setTimeout(() => {  
      console.log('setTimeout 1')
      Promise.resolve()
          .then(() => {
            console.log('promise 3')
          })
          .then(() => {
            console.log('promise 4')
          })
          .then(() => {
            setTimeout(() => {
              console.log('setTimeout 2')
              Promise.resolve()
                  .then(() => {
                    console.log('promise 5')
                  })
                  .then(() => {
                    console.log('promise 6')
                  })
                  .then(() => {
                    clearInterval(interval)
                  })
            }, 0)
          })
    }, 0)
    
    Promise.resolve()
        .then(() => {  
            console.log('promise 1')
        })
        .then(() => {
            console.log('promise 2')
        })
    

    当然,这是一道为了考察知识而特意出的题目,其实并不算实际问题,而真正让我想要继续了解 js 事件机制的原因是最近对 Promise 的新体会:Promise 之所以无法使用 catch 捕获 setTimeout 回调中的错误,是因为 Promise 的 then/catch 是在 setTimeout 之前执行的。

    同为异步事件,为何有先后之分呢?

    因为它们是不同的异步事件类型:Microtasks 与 Macrotasks

    众所周知

    js 的 event loop 如下:


    image.png

    主线程会先执行同步代码,遇到异步代码则将其插入异步“任务队列”。待主线程空了,再会去读取"任务队列",这就是JavaScript的运行机制。这个过程会不断重复。

    可是

    关键在于“任务队列”不止一个,它分为 Microtasks queue 与 Macrotasks queue,分别存放着 Microtasks 与 Macrotasks:

    Microtasks:
    • process.nextTick
    • promise
    • Object.observe
    • MutationObserver
    Macrotasks:
    • setTimeout
    • setInterval
    • setImmediate
    • I/O
    • UI渲染

    而且,一个 event loop (事件循环) 会包含多个 Macrotasks queue,也就是我们常说的 task queue (事件队列),但只包含一个 Microtasks queue,异步事件会根据自己的事件类型分别放入不同的 queue。所以有

    1. task queue == macrotask queue != microtask queue
    2. event loop = {
      macrotask queue list: [macrotask queue0, macrotask queue1, ...],
      microtask queue: ...,
    }
    

    理解了这些定义之后,再看执行原理:

    事件循环的顺序,决定了JavaScript代码的执行顺序。它从script(整体代码)开始第一次循环。之后全局上下文进入函数调用栈。直到调用栈清空(只剩全局),然后执行所有的microtasks。当所有可执行的microtasks执行完毕之后。循环再次从macrotasks开始,找到其中一个任务队列执行完毕,然后再执行所有的microtasks,这样一直循环下去。

    翻译过来就是,先执行 Microtasks queue 中的所有 Microtasks,再挑一个 Macrotasks queue 来执行其中所有 Macrotasks,然后继续执行 Microtasks queue 中的所有 Microtasks,再挑一个 Macrotasks queue 来执行其中所有 Macrotasks ……

    这也就解释了,为什么同一个事件循环中的 Microtasks 会比 Macrotasks 先执行。

    还要注意一点:

    包裹在一个 script 标签中的 js 代码也是一个 Macrotasks

    答案

    start 
    promise 1 
    promise 2 
    setInterval 
    setTimeout 1 
    promise 3 
    promise 4 
    setInterval 
    setTimeout 2 
    promise 5 
    promise 6
    

    相关文章

      网友评论

          本文标题:Microtasks 与 Macrotasks

          本文链接:https://www.haomeiwen.com/subject/zjqtmxtx.html