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

相关推荐
老前端的功夫1 天前
Vue 3 性能深度解析:从架构革新到运行时的全面优化
javascript·vue.js·架构
天天扭码1 天前
如何实现流式输出?一篇文章手把手教你!
前端·aigc·ai编程
前端 贾公子1 天前
vue移动端适配方案 === postcss-px-to-viewport
前端·javascript·html
GISer_Jing1 天前
AI营销增长:4大核心能力+前端落地指南
前端·javascript·人工智能
GSDjisidi1 天前
东京IT软件会社-(株)GSD|多种技术栈募集,高度人才+20分
开发语言·面试·职场和发展
明远湖之鱼1 天前
一种基于 Service Worker 的渐进式渲染方案的基本原理
前端
前端小端长1 天前
Vue 中 keep-alive 组件的原理与实践详解
前端·vue.js·spring
FeelTouch Labs1 天前
Nginx核心架构设计
运维·前端·nginx
雪球工程师团队1 天前
别再“苦力”写后台,Spec Coding “跑” 起来
前端·ai编程