Q3: 什么是调用栈(Call Stack)、任务队列(Task Queue)和微任务队列(Microtask Queue)?process.nextTick

30 阅读2分钟

Node.js 面试题详细答案 - Q3

Q3: 什么是调用栈(Call Stack)、任务队列(Task Queue)和微任务队列(Microtask Queue)?process.nextTick 和 Promise 哪个优先级更高?

调用栈(Call Stack)

调用栈是 JavaScript 引擎用来追踪函数调用的数据结构,遵循 LIFO(后进先出)原则。

function first() {
  console.log('first 开始')
  second()
  console.log('first 结束')
}

function second() {
  console.log('second 开始')
  third()
  console.log('second 结束')
}

function third() {
  console.log('third 执行')
}

first()

// 调用栈执行过程:
// 1. first() 入栈
// 2. second() 入栈
// 3. third() 入栈
// 4. third() 出栈
// 5. second() 出栈
// 6. first() 出栈

任务队列(Task Queue / Macro Task Queue)

任务队列存储宏任务,包括 setTimeout、setInterval、setImmediate、I/O 操作等。

console.log('1. 同步代码')

setTimeout(() => {
  console.log('2. setTimeout 回调')
}, 0)

setTimeout(() => {
  console.log('3. 另一个 setTimeout 回调')
}, 0)

console.log('4. 同步代码结束')

// 输出:
// 1. 同步代码
// 4. 同步代码结束
// 2. setTimeout 回调
// 3. 另一个 setTimeout 回调

微任务队列(Microtask Queue)

微任务队列存储微任务,包括 Promise、process.nextTick、queueMicrotask 等。

console.log('1. 同步代码')

Promise.resolve().then(() => {
  console.log('2. Promise 回调')
})

queueMicrotask(() => {
  console.log('3. queueMicrotask 回调')
})

console.log('4. 同步代码结束')

// 输出:
// 1. 同步代码
// 4. 同步代码结束
// 2. Promise 回调
// 3. queueMicrotask 回调

优先级比较

process.nextTick 优先级最高,其次是 Promise,最后是宏任务。

console.log('1. 同步代码开始')

setTimeout(() => {
  console.log('2. setTimeout')
}, 0)

setImmediate(() => {
  console.log('3. setImmediate')
})

Promise.resolve().then(() => {
  console.log('4. Promise')
})

process.nextTick(() => {
  console.log('5. process.nextTick')
})

queueMicrotask(() => {
  console.log('6. queueMicrotask')
})

console.log('7. 同步代码结束')

// 输出顺序:
// 1. 同步代码开始
// 7. 同步代码结束
// 5. process.nextTick (最高优先级)
// 4. Promise
// 6. queueMicrotask
// 2. setTimeout
// 3. setImmediate

详细优先级示例

// 复杂优先级示例
console.log('=== 开始 ===')

// 宏任务
setTimeout(() => console.log('setTimeout 1'), 0)
setImmediate(() => console.log('setImmediate 1'))

// 微任务
Promise.resolve().then(() => {
  console.log('Promise 1')

  // 在微任务中创建新的微任务
  process.nextTick(() => console.log('nextTick in Promise'))
  Promise.resolve().then(() => console.log('Promise in Promise'))
})

process.nextTick(() => {
  console.log('nextTick 1')

  // 在 nextTick 中创建新的微任务
  process.nextTick(() => console.log('nextTick in nextTick'))
  Promise.resolve().then(() => console.log('Promise in nextTick'))
})

console.log('=== 结束 ===')

// 输出:
// === 开始 ===
// === 结束 ===
// nextTick 1
// nextTick in nextTick
// Promise in nextTick
// Promise 1
// nextTick in Promise
// Promise in Promise
// setTimeout 1
// setImmediate 1

事件循环中的执行顺序

// 事件循环执行顺序
function demonstrateEventLoop() {
  console.log('1. 同步代码')

  // 宏任务
  setTimeout(() => console.log('2. setTimeout'), 0)
  setImmediate(() => console.log('3. setImmediate'))

  // 微任务
  process.nextTick(() => {
    console.log('4. process.nextTick')

    // 嵌套微任务
    Promise.resolve().then(() => {
      console.log('5. Promise in nextTick')
    })
  })

  Promise.resolve().then(() => {
    console.log('6. Promise')

    // 嵌套微任务
    process.nextTick(() => {
      console.log('7. nextTick in Promise')
    })
  })

  console.log('8. 同步代码结束')
}

demonstrateEventLoop()

// 输出:
// 1. 同步代码
// 8. 同步代码结束
// 4. process.nextTick
// 7. nextTick in Promise
// 6. Promise
// 5. Promise in nextTick
// 2. setTimeout
// 3. setImmediate

实际应用场景

// 利用优先级优化性能
function processData(data) {
  console.log('开始处理数据')

  // 使用 process.nextTick 确保在下一个事件循环前执行
  process.nextTick(() => {
    console.log('数据预处理完成')

    // 使用 Promise 处理异步操作
    return Promise.resolve()
      .then(() => {
        console.log('异步处理开始')
        // 模拟异步操作
        return new Promise((resolve) => setTimeout(resolve, 100))
      })
      .then(() => {
        console.log('异步处理完成')
      })
  })
}

processData('test data')

总结

  • 调用栈:存储函数调用,LIFO 结构
  • 任务队列:存储宏任务(setTimeout、setImmediate 等)
  • 微任务队列:存储微任务(Promise、process.nextTick 等)
  • 优先级:process.nextTick > Promise > 宏任务
  • 理解这些概念对于编写高效的异步代码至关重要