🚀 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 与微任务。

相关推荐
鹤鸣的日常2 小时前
Vue + element plus 二次封装表格
前端·javascript·vue.js·elementui·typescript
JarvanMo2 小时前
Flakeproof - 自动化 Flutter 的用户体验 (UX) 测试
前端
北慕阳2 小时前
速成Vue,自己看
前端·javascript·vue.js
shanyanwt2 小时前
1分钟解决iOS App Store上架图片尺寸问题
前端·ios
摇滚侠2 小时前
HTML5,CSS3,开启浮动布局后,主轴和侧轴的概念
前端·css3·html5
少云清2 小时前
【软件测试】4_基础知识 _HTML
前端·html
Want5952 小时前
HTML跳动的爱心①
前端·html
小兔崽子去哪了2 小时前
mitt 跨多层组件甚至兄弟组件通信
前端
007php0072 小时前
Redis面试题解析:Redis的数据过期策略
java·网络·redis·缓存·面试·职场和发展·php