likes
comments
collection
share

浅谈JS宏任务&微任务&Event Loop

作者站长头像
站长
· 阅读数 17

先来看段代码

// 示例代码1
     console.log("start", 1)
    // 宏任务
    setTimeout(() => {
      setTimeout(() => {
        console.log("setTimeout", 12)
      }, 0)
      Promise.resolve().then(() => {
        console.log("Promise", 8)
      })
    }, 0)

    Promise.resolve().then(() => {
      console.log("Promise,", 3)
      Promise.resolve().then(() => {
        console.log("Promise", 5)
        Promise.resolve().then(() => {
          console.log("Promise", 7)
        })
      })
      setTimeout(() => {
        console.log("setTimeout", 10)

      }, 0)
    })
    Promise.resolve().then(() => {
      console.log("Promise", 4)
      Promise.resolve().then(() => {
        console.log("Promise", 6)
      })
      setTimeout(() => {
        console.log("setTimeout", 11)

      }, 0)
    })

    setTimeout(() => {
      console.log("setInerval", 9)
    }, 0);
    console.log("start2", 2)

    
    // 执行结果
    // start 1
    // start2 2
    // Promise 3
    // Promise 4
    // Promise 5
    // Promise 6
    // Promise 7
    // Promise 8
    // setInerval 9
    // setTimeout 10
    // setTimeout 11
    // setTimeout 12
  • 为什么会出现这样的执行顺序呢?

因为JS执行机制就是这样的 我也莫得办法。。。

  • JS执行机制

提到js执行机制那就必须得提几个概念、宏任务、微任务、异步任务、同步任务,以及事件循环,在了解这些概念之前 先看一张示例图

浅谈JS宏任务&微任务&Event Loop

  • 先说一下什么是宏任务、微任务、同步异步任务

宏任务:

  • 整体的script代码
  • setTimeout
  • setInterval
  • setImmediate
  • I/O (比如Ajax操作从网络读取数据)
  • UI render

微任务:

  • process.nextTick
  • Promise
  • Async/Await(实际就是promise)
  • MutationObserver(html5新特性)

同步任务:

示例代码1 console.log("start", 1) 就是一个同步任务

异步任务:

setTimeout、setInterval、 I/O、Promise、Async/Await等等

示例代码1 中setTimeout promis.then 都属于异步任务。且setTimeout 属于宏任务 promise.then 属于微任务

事件循环

上述示例图中 这部分 宏任务循环执行就是事件循环 宏任务=> 微任务 => 宏任务

浅谈JS宏任务&微任务&Event Loop

看到这里如果还不懂 没关系,我们来借助示例代码1 来理解

  • 首先 我们应该先明确一个执行概念,在同步代码>微任务队列>宏任务队列,在同步代码执行完毕之后微任务优先级高于宏任务 然后我们在区分哪些代码是同步 哪些代码是异步、在示例一代码中 除console 之外 其余的都是异步代码,所以可以明确两个同步代码的执行顺序
 console.log("start", 1)
console.log("start2", 2)
  • 明确了所有同步代码的执行顺序之后,我们在区分 哪些任务是微任务 与宏任务
  1. 微任务
// promise.then 属于微任务
 Promise.resolve().then(() => {
      console.log("Promise,", 3)
      setTimeout(() => {
        console.log("setTimeout", 8)

      }, 0)
    })
// promise.then 属于微任务
Promise.resolve().then(() => {
  console.log("Promise", 4)
  Promise.resolve().then(() => {
    console.log("Promise", 5)
  })
  setTimeout(() => {
    console.log("setTimeout", 9)
  }, 0)
})  

  1. 宏任务
// setTimeout属于宏任务
setTimeout(() => {
  setTimeout(() => {
    console.log("setTimeout", 10)
  }, 0)
  Promise.resolve().then(() => {
    console.log("Promise", 6)

  })
}, 0)
 // setTimeout属于宏任务
setTimeout(() => {
  console.log("setInerval", 7)
}, 0);
  • 综上所述 当同步任务执行完毕之后 此时的任务队列应该是这样的

微任务:[
Promise.resolve().then(() => {
      console.log("Promise,", 3)
      setTimeout(() => {
        console.log("setTimeout", 8)

      }, 0)
    }),
Promise.resolve().then(() => {
  console.log("Promise", 4)
  Promise.resolve().then(() => {
    console.log("Promise", 5)
  })
  setTimeout(() => {
    console.log("setTimeout", 9)
  }, 0)
}),  ]

 宏任务:[
setTimeout(() => {
  setTimeout(() => {
    console.log("setTimeout", 10)
  }, 0)
  Promise.resolve().then(() => {
    console.log("Promise", 6)

  })
}, 0),
setTimeout(() => {
  console.log("setInerval", 7)
}, 0);
 ]

当同步任务执行完毕之后,接下来会执行微任务队列,微任务队列执行完毕之后,才会进入事件循环状态,执行宏任务

  • PS:当执行微任务时,微任务内含有宏任务,那么会将宏任务push到宏任务队列中,如果微任务中含有微任务将进行同步执行,代码如下

// 当微任务中含有宏任务
Promise.resolve().then(() => {
      console.log("Promise,", 3)
      // 此时遇到setTimeout不会立刻执行,setTimeout为宏任务,会push到宏任务中按序执行
      setTimeout(() => {
        console.log("setTimeout", 8)

      }, 0)

    })
    
// 当微任务中含有微任务
Promise.resolve().then(() => {
    console.log("Promise", 4)
    // 此刻遇到promise.then为微任务,立刻执行此微任务
    Promise.resolve().then(() => {
        console.log("Promise", 5)
    })
    // 此时遇到setTimeout不会立刻执行,setTimeout为宏任务,会push到宏任务中按序执行
    setTimeout(() => {
        console.log("setTimeout", 9)
    }, 0)
}), 

  • PS:当执行宏任务时,宏任务内含有宏任务,那么会将宏任务push到宏任务队列中,如果宏任务中含有微任务将进行同步执行,代码如下
setTimeout(() => {
// 遇到宏任务 push到宏任务队列中
  setTimeout(() => {
    console.log("setTimeout", 10)
  }, 0)
  // 遇到微任务,继续执行
  Promise.resolve().then(() => {
    console.log("Promise", 6)
  })
}, 0),

如果看到这还没能理解代码示例1中的执行顺序 也没关系,我们用js代码实现一下整个js执行机制

// javascriptCode 必做所有js代码
// wTask 我们比做微任务队列
// hTask 我们比做宏任务队列
// Javascript 用来执行我们的js代码
// codeList 值: key-代码类型 code-执行代码 child-宏任务或者微任务里的子任务
// 将示例代码转化为得到javascriptCode如下

 let javascriptCode = [{
      key: 0,
      code: "start1"
    }, {
      key: 2,
      code: "",
      child: [
        {
          key: 2,
          code: "setTimeout:12",
        },
        {
          key: 1,
          code: "Promise:8",
        }
      ]
    },
    {
      key: 1,
      code: "Promise:3",
      child: [
        {
          key: 1,
          code: "Promise:5",
          child: [
            {
              key: 1,
              code: "Promise:7",
            },
          ]
        },

        {
          key: 2,
          code: "setTimeout:10",
        }
      ]
    },
    {
      key: 1,
      code: "Promise:4",
      child: [
        {
          key: 1,
          code: "Promise:6",
        },
        {
          key: 2,
          code: "setTimeout:11",
        }
      ]
    },
    {
      key: 2,
      code: "setInerval:9",

    },
    {
      key: 0,
      code: "start2:2",

    },]
  • 接下来我们用一个javascript类 执行我们的javascriptCode

// 首先要明确 我们的执行顺序是 同步代码=>微任务=>进入事件循环=>宏任务=>微任务=> 事件循环

   class Javascript {
      constructor(codeList) {
        this.wTask = [] // 微任务队列
        this.hTask = [] // 宏任务队列
        this.codeList = codeList || [] // js代码
        this.init() // 初始化
      }
      init() {
        
        // 先执行同步任务 然后将异步任务放入对应队列
        this.codeList.forEach(item => {
          if (item.key === 0 && item.code) {
            // 同步任务
            console.log("同步任务直接执行", item.code)
          }
          if (item.key === 1) {
          // 微任务
            this.wTask.push(item)
          }
          if (item.key === 2) {
            // 宏任务
            this.hTask.push(item)
          }


        })
        // 同步任务执行完毕之后再执行微任务
        this.implementTask(1)

      }

      // 当宏任务变化时 触发任务执行机制
      implementTask(type) {
        // type 决定宏任务还是微任务
        const list = type === 1 ? this.wTask : this.hTask
        if (list?.length > 0) {
          const currentTask = list[0]
          // 先执行同步任务
          if (currentTask.code) {
            console.log("微任务里的同步任务", currentTask.code)
          }
          // 如果包含子任务
          if (currentTask.child) {
            currentTask.child.forEach(item => {
              if (item.key === 1) {
                this.wTask.push(item)
              }
              if (item.key === 2) {
                this.hTask.push(item)
              }
            })
          }
          if (type === 1) {
            this.wTask.shift()
          } else {
            this.hTask.shift()
          }

          if (this.wTask.length > 0) {
            this.implementTask(1)
          } else {
            this.implementTask(2)
          }
        }

      }

转载自:https://juejin.cn/post/7098324584155316232
评论
请登录