JavaScript Promise 从入门到精通(一):为什么需要 Promise 以及它是什么

从一个真实的故事开始

假设你要做一个用户数据展示页面,需要:

  1. 先获取用户信息
  2. 根据用户 ID 获取订单列表
  3. 根据第一个订单 ID 获取订单详情
  4. 发送通知说"加载完成"

听起来很简单对吧?在没有 Promise 的年代,代码是这样的:

js 复制代码
getUser(userId, (err, user) => {
  if (err) {
    console.error(err);
    return;
  }
  getOrders(user.id, (err, orders) => {
    if (err) {
      console.error(err);
      return;
    }
    getOrderDetail(orders[0].id, (err, detail) => {
      if (err) {
        console.error(err);
        return;
      }
      sendNotification(detail, (err, result) => {
        if (err) {
          console.error(err);
          return;
        }
        console.log('完成');
      });
    });
  });
});

这就是臭名昭著的回调地狱(Callback Hell)------ 代码像金字塔一样向右无限生长。如果你做过实际项目,一定写过比这嵌套更深的代码。问题是:

  • 🔴 可读性极差:眼睛跟着缩进跑
  • 🔴 每个回调都要重复错误处理:太啰嗦
  • 🔴 try/catch 完全失效:异步错误抓不到
  • 🔴 想做并发? 比如同时请求三个独立接口 ------ 用回调写起来非常痛苦

还有更隐蔽的问题:信任问题 。你把回调函数交给了第三方的异步方法 ------ 它什么时候调用你的回调?调用几次?会不会因为 bug 调了两次?会不会根本不调用?你完全控制不了

Promise 登场:一张小票的比喻

你去奶茶店点单,店员给你一张小票 。这不是奶茶,但它代表了 你未来会拿到的那杯奶茶。你可以拿着小票继续逛街(同步代码继续执行),等奶茶做好后凭小票去取(.then() 拿到结果)。

Promise 就是这个"小票"------ 一个代表未来值的对象。它让你:

  • 不用 把回调交给第三方(你持有 Promise,决定何时 .then()
  • 可以链式串联多个异步操作
  • 可以统一处理错误
  • 可以轻松组合多个异步操作

Promise 的三种状态

Promise 是一个不可逆的状态机

yaml 复制代码
                    ┌──→ fulfilled(已成功)
                    │     PromiseValue: 成功值
pending(进行中)──→─┤
                    │
                    └──→ rejected(已失败)
                          PromiseValue: 失败原因

核心规则:状态一旦确定,永不改变。 就像一杯奶茶,做完了就是做完了,不能再变成"正在做"。

js 复制代码
// 创建 Promise
const promise = new Promise((resolve, reject) => {
  // 这里的代码是**同步执行**的!
  setTimeout(() => {
    const success = Math.random() > 0.5;
    if (success) {
      resolve('🎉 成功了');
    } else {
      reject('💥 失败了');
    }
  }, 1000);
});

// 消费 Promise
promise
  .then(result => console.log('then:', result))   // 成功走这里
  .catch(error => console.error('catch:', error))  // 失败走这里
  .finally(() => console.log('无论成败都执行'));    // 最终清理

理解 .then():链式调用的魔法

很多人刚开始用 Promise 时会犯一个错:把 .then() 当成"添加回调"。

不对。 .then() 永远返回一个新 Promise。这才是链式调用的基础:

js 复制代码
fetchUser(1)                              // Promise<User>
  .then(user => fetchOrders(user.id))     // Promise<Order[]>
  .then(orders => getDetail(orders[0]))   // Promise<Detail>
  .then(detail => console.log(detail))    // Promise<undefined>
  .catch(err => console.error('任何一环出错都会到这', err));

每一环 .then() 返回的 Promise,其状态由回调函数的返回值决定:

  • 返回普通值 → 自动包装为 fulfilled 的 Promise
  • 返回 Promise → 沿用那个 Promise 的状态
  • 抛出异常 → 返回 rejected 的 Promise

正是因为 .then() 总是返回新 Promise,链上的错误才能"冒泡"到最后的 .catch()

async/await:让异步代码看起来像同步的

async/await 本质上是 Promise 的语法糖 ------ 没有引入任何新能力,只是写法上的优化

js 复制代码
// Promise 链式写法
function getUserData(id) {
  return fetchUser(id)
    .then(user => fetchOrders(user.id))
    .then(orders => ({ user, orders }));
}

// async/await 写法 ------ 一样的事,更直观
async function getUserData(id) {
  const user = await fetchUser(id);
  const orders = await fetchOrders(user.id);
  return { user, orders };
}

几个关键规则:

js 复制代码
// 1. async 函数总是返回 Promise
async function fn() { return 42; }
fn();  // Promise {<fulfilled>: 42}

// 2. await 只能写在 async 函数里
// 3. await 后面的代码 = .then() 回调

// 4. 用熟悉的 try/catch 处理错误
async function getData() {
  try {
    const data = await fetch('/api');
    return data.json();
  } catch (err) {
    console.error('请求失败', err);
    return null; // 降级处理
  }
}

本篇总结

概念 一句话
Promise 代表未来的异步结果,像奶茶店的小票
三种状态 pending → fulfilled / rejected,不可逆
.then() 永远返回新 Promise,链式调用基础
async/await Promise 的语法糖,写法更像同步代码

下一篇我们深入 Promise 的静态方法(all / allSettled / race / any),看看如何优雅地协调多个异步操作。


相关推荐
qdkfz1 小时前
JavaScript 如何解析 Outlook .msg 文件 —— @kenjiuno/msgreader
前端·javascript
张元清2 小时前
React useReducedMotion Hook:尊重 prefers-reduced-motion(2026)
javascript·react.js
用户298698530144 小时前
前端 Excel 处理进阶:React 中合并与取消合并单元格的实现
前端·javascript·react.js
触底反弹4 小时前
面试被问 RAG 只能说出六个字?这篇用 100 行 Node.js 代码帮你彻底搞懂
javascript·人工智能·面试
Highcharts4 小时前
@highcharts/react完整技术指南|新版Highcharts React使用、迁移、SSR、TS 优化全解
前端·javascript
gis开发之家5 小时前
vue3组合式函数(Composables)的艺术——将业务逻辑从UI组件中连根拔起
前端·javascript·vue.js·前端框架·vue3·前端工程化·前端架构
liuniansilence5 小时前
一次讲透 H5 唤起原生 App:Scheme 协议、平台限制与生产级封装
前端·javascript