Promises:优雅处理异步操作

16 阅读3分钟

上一期我们见识了回调地狱的恐怖,今天终于迎来拯救者——Promise

Promise 是 JavaScript 异步编程的真正转折点,它几乎完美解决了回调的三大痛点:
可读性差 → 错误处理麻烦 → 难以组合

1. Promise 到底是什么?

一句话概括:

Promise 是一个代表未来某个值的对象,它有三种状态,且状态一旦改变就不可逆转。

三种状态:

  • pending(待定)→ 初始状态
  • fulfilled(已兑现/成功)→ resolve(value)
  • rejected(已拒绝/失败)→ reject(reason)
const p = new Promise((resolve, reject) => {
  // 做一些异步操作...
  setTimeout(() => {
    const success = Math.random() > 0.5;
    if (success) {
      resolve("成功啦!数据是:666");    // 进入 fulfilled
    } else {
      reject(new Error("出错了!"));       // 进入 rejected
    }
  }, 1500);
});

2. Promise 的核心用法:then / catch / finally

p
  .then(value => {
    console.log("成功:", value);
    return "上一步的结果可以继续传下去"; // 可以链式
  })
  .then(nextValue => {
    console.log("第二层:", nextValue);
  })
  .catch(err => {
    console.error("任意一层出错都会到这里:", err);
  })
  .finally(() => {
    console.log("不管成功失败我都会执行~");
  });

最重要的特性

  1. 链式调用:每个 then 都可以返回新值(或新 Promise),形成流畅的链
  2. 错误穿透:只要中间不被 catch 住,错误会一直向下传递到最后一个 catch
  3. 只有一个 catch 就够:不用像回调那样每层都写错误处理

3. Promise 静态方法(非常实用)

// 全部成功才成功,有一个失败就失败
Promise.all([p1, p2, p3])
  .then(results => console.log("全部成功", results))
  .catch(err => console.log("至少有一个失败了", err));

// 谁先完成用谁(常用于竞速、取最快响应)
Promise.race([p1, p2, p3])
  .then(first => console.log("最快完成的是:", first));

// 全部结算完才结束(不管成功失败)ES2020+
Promise.allSettled([p1, p2, p3])
  .then(results => {
    // results 是 [{status:"fulfilled", value:xx}, {status:"rejected", reason:xx}] 的数组
  });

// 只要有一个成功就成功(ES2023+)
Promise.any([p1, p2, p3])
  .then(firstSuccess => console.log("至少有一个成功"))
  .catch(() => console.log("全部都失败了"));

4. 真实业务场景对比:登录 → 获取信息 → 获取推荐

回调地狱版(上一期见过):

login(..., (err, token) => {
  if (err) return;
  getUserInfo(token, (err, user) => {
    if (err) return;
    getRecommendations(user.level, (err, products) => {
      if (err) return;
      render(products);
    });
  });
});

Promise 版(优雅太多):

login(username, password)
  .then(token => getUserInfo(token))
  .then(user => getRecommendations(user.level))
  .then(products => renderProducts(products))
  .catch(err => {
    console.error("登录流程某一步失败:", err);
    showErrorToast("出错了,请稍后重试");
  });

5. Promise 常见陷阱(新手必看)

// 陷阱1:忘了 return 会断链
.then(() => {
  doSomething();          // ← 没 return,下面拿不到结果
})
.then(result => {         // ← result 是 undefined
  // ...
});

// 陷阱2:Promise 里抛错不会自动 reject(除非在 executor 里)
new Promise((resolve) => {
  throw new Error("同步错误");   // 这个错误不会被 catch 到!
});

// 正确做法:
new Promise((resolve, reject) => {
  try {
    // ...
  } catch(e) {
    reject(e);
  }
});

6. 小结:Promise 带来的革命性改变

  • 告别缩进地狱,代码从“向右发展”变成“向下流动”
  • 统一的错误处理机制
  • 强大的组合能力(all / race / allSettled / any)
  • 成为现代异步的基石(几乎所有新 API 都返回 Promise)

可以说:学会 Promise = 掌握了现代 JavaScript 异步的 70%

下一期,我们将迎来目前最推荐的写法——async/await
它让异步代码看起来像同步代码一样清晰,同时保留了 Promise 的全部强大能力。

我们下期见~