MST

星途 面试题库

面试题:深度解析JavaScript异步编程中的微任务队列与Promise

详细说明JavaScript事件循环机制中微任务队列与Promise的关系。假设你有一段包含多个Promise链式调用、setTimeout以及微任务(如Promise.resolve().then())的代码,请分析其执行顺序,并解释原因。
33.5万 热度难度
编程语言JavaScript

知识考点

AI 面试

面试题答案

一键面试

微任务队列与Promise的关系

  1. Promise是微任务的一种典型代表:在JavaScript事件循环机制中,微任务队列用于存放需要在当前执行栈清空后,下一轮宏任务执行前优先执行的任务。Promise的then方法回调会被放入微任务队列。当一个Promise被解决(resolved)或被拒绝(rejected)时,通过thencatch注册的回调函数不会立即执行,而是被添加到微任务队列中。
  2. 微任务队列保证Promise回调的有序执行:由于微任务队列的特性,所有Promise产生的微任务会按照它们添加到队列的顺序依次执行。这确保了Promise链式调用中的回调函数按照预期的顺序依次执行,从而实现了异步操作的顺序控制。

代码执行顺序分析及原因

假设代码如下:

console.log('start');
setTimeout(() => {
    console.log('setTimeout callback');
}, 0);
Promise.resolve()
   .then(() => {
        console.log('Promise 1st then');
        return Promise.resolve();
    })
   .then(() => {
        console.log('Promise 2nd then');
    });
console.log('end');
  1. 初始执行
    • 首先,JavaScript引擎从全局执行上下文开始执行代码。打印start
    • 遇到setTimeout,它是一个宏任务,会被放到宏任务队列中。这里设置延迟为0,但它不会立即执行,而是等待合适时机被放入执行栈。
    • 遇到Promise.resolve().then(...)Promise.resolve()立即解决,其then回调被放入微任务队列。
    • 打印end。此时全局执行栈清空。
  2. 微任务执行阶段
    • 因为当前执行栈为空,事件循环机制开始处理微任务队列。它会依次执行微任务队列中的任务。
    • 首先执行Promise 1st then回调,打印Promise 1st then,并返回一个新的已解决的Promise。这个新Promise的then回调(Promise 2nd then)又被放入微任务队列。
    • 然后执行Promise 2nd then回调,打印Promise 2nd then。此时微任务队列清空。
  3. 宏任务执行阶段
    • 微任务队列清空后,事件循环机制从宏任务队列中取出一个宏任务放入执行栈执行。这里setTimeout回调是宏任务队列中唯一的任务,执行它,打印setTimeout callback

所以最终的输出顺序是:start -> end -> Promise 1st then -> Promise 2nd then -> setTimeout callback。原因是事件循环机制先执行完当前执行栈的同步代码,然后处理微任务队列,最后才处理宏任务队列。Promise的then回调作为微任务,会在宏任务之前执行,而setTimeout作为宏任务会在微任务执行完后才执行。