记录一道async/await面试题

82 阅读2分钟
// 基础异步函数,返回数字1
async function m1() {
  return 1; // 自动包装为Promise.resolve(1)
}

// 中级异步函数,等待m1的结果并打印
async function m2() {
  const n = await m1();
  console.log(n); // 打印从m1获得的值
}

// 高级异步函数,等待m2的结果并尝试计算
async function m3() {
  const n = await m2();
  console.log(n * 2); // 尝试对m2的结果进行计算
}

// 第一次调用m3并附加then回调
m3().then((data) => {
  console.log(data);
});

// 第二次直接调用m3
m3();

// 同步日志输出
console.log(3);

执行流程详解

执行阶段划分

  1. 同步执行阶段

    • 执行所有同步代码
    • 注册异步任务
  2. 微任务队列处理阶段

    • 按顺序处理所有微任务

详细执行步骤

步骤操作说明
0第一次调用m3()开始第一个异步调用链
1解析m3()中的await m2()创建Promise并等待m2()
2进入m2(),解析await m1()创建Promise并等待m1()
3m1()返回Promise.resolve(1)立即解析为值1
4将console.log(1)加入微队列来自m2()的await解析
5m2()返回未完成的Promise等待console.log执行
6m3()继续等待m2()完成
7第二次调用m3()开始第二个异步调用链
8重复步骤1-6创建第二个调用链
9-13类似第一个调用链的处理
14执行console.log(3)同步代码优先执行
15-16执行两个console.log(1)从微队列取出并执行
17-18将两个console.log(NaN)加入微队列因为m2()返回undefined
19-20执行两个console.log(NaN)undefined*2=NaN
21将console.log(undefined)加入微队列来自第一个m3()的then回调
22执行console.log(undefined)m3()没有返回值

关键概念说明

  1. async函数行为‌:

    • 总是返回Promise
    • return值会被自动包装为Promise.resolve()
    • 无return时默认返回Promise.resolve(undefined)
  2. await机制‌:

    • 暂停当前async函数执行
    • 等待右侧表达式解析完成
    • 将后续代码包装为微任务
  3. 执行顺序规则‌:

    • 同步代码 > 微任务 > 宏任务
    • 多个微任务按注册顺序执行

最终输出结果

3
1
1
NaN
NaN
undefined

问题分析

  1. NaN产生原因‌:

    • m2()没有返回值,导致n为undefined
    • undefined * 2 = NaN
  2. undefined输出‌:

    • m3()没有返回值
    • then回调收到的data是undefined
  3. 执行顺序保证‌:

    • console.log(3)最先执行(同步代码)
    • 然后是微任务队列中的日志
    • 最后是then回调的日志