也说JavaScript事件循环

179 阅读2分钟

笔试题——JavaScript事件循环机制(event loop、macrotask、microtask)有感

对于js事件循环机制,顶部那篇文章写的很好,但我认为例子对于初学者不是太直观,因此放出下面这个例子,如有哪些地方说的不对,敬请指出:

代码1
/* test.js */

for(let i = 0; i < 3; i++) {
  setTimeout(_ => {
    // 逐个推入 macrotasks 事件队列中
    console.log(`timeout: ${i}`)
  }, 0)

  new Promise((resolve) => {
    // new Promise中的语句立即执行
    console.log(`promise: ${i}`)
    resolve()
  }).then(_ => {
    // 逐个推入 microtasks 事件队列中,同一个 event loop 中,先于 macrotasks 执行
    console.log(`promise.then: ${i}`)
  })
  
  // 立即执行
  console.log(`immediate: ${i}`)
}
结果1
> node test.js

promise: 0
immediate: 0
promise: 1
immediate: 1
promise: 2
immediate: 2
then: 0
then: 1
then: 2
timeout: 0
timeout: 1
timeout: 2

还有一个例子,个人认为更加完整,但是代码比较繁琐比较丑,可以直接拉到底部看结果

代码2
/* test.js */

// utils
const PRE = {
  macrotask        : "macrotask        ",
  microtask        : "microtask        ",
  promiseImmediate : "promiseImmediate ",
  immediate        : "immediate        "
}

function setTimeoutLog(w, f) {
  setTimeout(_ => {
    console.log(`${PRE.macrotask}: ${w}`)
    
    if(f instanceof Function) {
      f(PRE.macrotask)
    }
  }, 0)
}

function promiseLog(w) {
  return new Promise((resolve) => {
    console.log(`${PRE.promiseImmediate}: ${w}`)
    resolve(PRE.microtask)
  })
}

function immediateLog(w) {
  console.log(`${PRE.immediate}: ${w}`)
}

// main
for(let i = 0; i < 3; i++) {
  setTimeoutLog(i, pre => {
    setTimeoutLog(`${pre} -> ${PRE.macrotask} ${i}`)
    promiseLog(`${pre} -> ${PRE.promiseImmediate} ${i}`)
  })

  promiseLog(i).then(pre => {
    setTimeoutLog(`${pre} -> ${PRE.macrotask} ${i}`)
    promiseLog(`${pre} -> ${PRE.promiseImmediate} ${i}`)
  })

  immediateLog(i)
}

结果2
> node test.js

promiseImmediate : 0
immediate        : 0
promiseImmediate : 1
immediate        : 1
promiseImmediate : 2
immediate        : 2

# 进入了 microtask 队列
promiseImmediate : microtask         -> promiseImmediate  0
promiseImmediate : microtask         -> promiseImmediate  1
promiseImmediate : microtask         -> promiseImmediate  2

# 进入了 macrotask 队列,按入列时的先后顺序依次执行队列
macrotask        : 0
promiseImmediate : macrotask         -> promiseImmediate  0
macrotask        : 1
promiseImmediate : macrotask         -> promiseImmediate  1
macrotask        : 2
promiseImmediate : macrotask         -> promiseImmediate  2
# microtask 执行时入列的 macrotask 
macrotask        : microtask         -> macrotask         0
macrotask        : microtask         -> macrotask         1
macrotask        : microtask         -> macrotask         2
# macrotask 执行时入列的 macrotask
macrotask        : macrotask         -> macrotask         0
macrotask        : macrotask         -> macrotask         1
macrotask        : macrotask         -> macrotask         2

不得不吐槽掘金的草稿保存机制了,添加一个标签就触发一次缓存,这没问题,但是,缓存期间禁用发布按钮并关闭发布框,这就有点。。。人家SF做的就讲理点。

参考

  1. 笔试题——JavaScript事件循环机制(event loop、macrotask、microtask) -- 掘金用户:立志搬砖造福生活
  2. JavaScript 运行机制详解:再谈Event Loop -- 阮一峰