🚀 Promise.then 与 async/await 到底差在哪?(这次彻底讲明白)

"Promise 是异步的,await 是同步写法。"

这句话你可能听了上百次,但------你真的懂它背后的执行原理吗?

在这个性能与体验并重的时代,如果你还把 async/await 当作"写法更舒服的 Promise",那你可能错过了它真正的威力。

今天,我们就来一次彻底的拆解

Promise 与 async/await ------到底差在哪?为什么看起来同步的 await,依然是异步的?

一、Promise 是什么?本质是异步状态机

从规范角度看,Promise 就是一个异步状态机。它有三种状态:

状态 英文 含义
待定 pending 任务尚未完成
已兑现 fulfilled 成功完成,返回结果
已拒绝 rejected 发生错误,返回原因

一旦状态从 pending 变为 fulfilledrejected,就不可逆

js 复制代码
const p = new Promise((resolve, reject) => {
  setTimeout(() => resolve('done'), 1000);
});

p.then(value => console.log(value)); // done

二、事件循环基础:宏任务与微任务

在讲 Promise 前,必须先搞清楚 JS 的执行机制:事件循环(Event Loop)

🔹 1. 什么是宏任务(Macro Task)?

宏任务是指每次事件循环中执行的主任务块,包括:

  • script(整段代码)
  • setTimeout
  • setInterval
  • setImmediate(Node.js)
  • I/O 操作 等

🔹 2. 什么是微任务(Micro Task)?

微任务是插入在一次宏任务结束之后、下一个宏任务开始之前 执行的任务。

常见的微任务包括:

  • Promise.then
  • queueMicrotask
  • MutationObserver

🔹 3. 执行顺序总结:

  1. 执行一个宏任务;
  2. 执行完后,清空当前所有微任务;
  3. 再开始下一个宏任务。
text 复制代码
┌──────────────────────────────────┐
│  开始执行 JS 脚本(第一个宏任务)     │
└───────────────┬──────────────────┘
                │
                ▼
       执行同步代码(主线程执行区)
                │
                ▼
       注册异步任务(定时器、Promise等)
                │
                ▼
┌──────────────────────────────────┐
│    宏任务执行完毕(本轮结束)        │
└───────────────┬──────────────────┘
                │
                ▼
       检查微任务队列(Microtasks)
       ├─ Promise.then 回调
       ├─ async/await 恢复点
       ├─ queueMicrotask 等
                │
                ▼
     清空所有微任务(一次性执行完)
                │
                ▼
┌──────────────────────────────────────────────┐
│     开始执行下一个宏任务(下一轮事件循环)         │
│   setTimeout、setInterval、I/O 等进入执行区     │
└──────────────────────────────────────────────┘

我们看一个例子:

js 复制代码
console.log('A');

setTimeout(() => console.log('B'), 0); // 宏任务
Promise.resolve().then(() => console.log('C')); // 微任务

console.log('D');

输出顺序:

css 复制代码
A
D
C
B

解释:

  • 整个脚本是第一个宏任务;
  • Promise.then 注册的回调进入微任务队列;
  • 宏任务(脚本)执行完后清空微任务 → 输出 C
  • 最后再执行下一个宏任务(setTimeout)→ 输出 B

三、then 的原理:注册微任务,异步回调

.then() 不会立刻执行,而是:

  1. 把成功/失败回调注册到微任务队列
  2. 返回一个新的 Promise,用于链式调用。
javascript 复制代码
Promise.resolve().then(() => console.log('microtask'));
console.log('sync');

// 输出:
// sync
// microtask

要点:

.then() 的回调属于微任务,

会在当前宏任务结束后立即执行。

四、async/await:Promise 的"同步写法"

async/await 是对 Promise 的高级封装。

  • async 函数会自动返回一个 Promise;
  • await 让代码"暂停",等 Promise 完成后继续执行。

示例:

js 复制代码
async function fetchData() {
  const res = await fetch('/api/data');
  const data = await res.json();
  console.log(data);
}

等价于:

js 复制代码
function fetchData() {
  return fetch('/api/data')
    .then(res => res.json())
    .then(data => console.log(data));
}

结论:

await 只是让异步代码"写起来像同步",但本质依旧是基于微任务的异步机制。

五、await 的底层执行逻辑

当 JS 引擎遇到 await 时,会这样做:

  1. 计算 await 后的表达式;

  2. 如果不是 Promise,直接返回值;

  3. 如果是 Promise:

    • 暂停当前 async 函数;
    • 把后续代码(await 之后的部分)放入微任务队列
    • 等 Promise 完成后,恢复执行。

举例:

js 复制代码
async function test() {
  console.log(1);
  await null;
  console.log(2);
}
test();
console.log(3);

输出:

text 复制代码
1
3
2

解释:

  • await null 会被转换为 Promise.resolve(null)
  • 当前函数暂停,后续 console.log(2) 放入微任务;
  • 先执行同步代码 13,再执行微任务 2

六、Promise.then vs async/await 对比总结

对比点 Promise.then() async/await
写法风格 链式调用 同步风格
返回值 新的 Promise 一个 Promise
错误处理 .catch() try...catch
可读性 回调嵌套 线性逻辑
执行机制 微任务回调 微任务暂停+恢复
底层实现 回调链 状态机封装 Promise

一句话总结:

await 是把 .then() 的回调逻辑拆成了"暂停 + 恢复执行"两步。

底层依然是 Promise 和微任务在驱动。

七、为什么有时 .then()await 先执行?

javascript 复制代码
async function main() {
  const a = await Promise.resolve('A');
  const b = await Promise.resolve('B');
  Promise.resolve('C').then(console.log);
  console.log(a, b);
}
main();

// 输出:
// C
// A B

解释:

  • .then() 的回调会立即注册微任务
  • await 的后续执行要等当前 await 完成后再注册;
  • 所以微任务队列中 .then 的任务排在更前。

小结

很多人会背结论:

"await 会暂停函数","then 会注册回调"...

但要真正写出高性能、无陷阱的异步代码,

你必须理解它们在 事件循环(Event Loop) 中的真实运行逻辑,同样也是面试中的技术要点。

一句话复盘:

then 是异步回调注册机制,
await 是暂停 + 恢复的语法糖,

它们的底层,都是 Promise 与微任务。

相关推荐
晚烛28 分钟前
CANN + 物理信息神经网络(PINNs):求解偏微分方程的新范式
javascript·人工智能·flutter·html·零售
saber_andlibert1 小时前
TCMalloc底层实现
java·前端·网络
逍遥德1 小时前
如何学编程之01.理论篇.如何通过阅读代码来提高自己的编程能力?
前端·后端·程序人生·重构·软件构建·代码规范
冻感糕人~1 小时前
【珍藏必备】ReAct框架实战指南:从零开始构建AI智能体,让大模型学会思考与行动
java·前端·人工智能·react.js·大模型·就业·大模型学习
程序员agions1 小时前
2026年,“配置工程师“终于死绝了
前端·程序人生
alice--小文子1 小时前
cursor-mcp工具使用
java·服务器·前端
晚霞的不甘1 小时前
揭秘 CANN 内存管理:如何让大模型在小设备上“轻装上阵”?
前端·数据库·经验分享·flutter·3d
小迷糊的学习记录2 小时前
0.1 + 0.2 不等于 0.3
前端·javascript·面试
程序员敲代码吗2 小时前
面试中sessionStorage问题引发深度探讨
面试·职场和发展
空&白2 小时前
vue暗黑模式
javascript·vue.js