JavaScript 中的 Promise API 全面解析

在现代 JavaScript 开发中,异步编程是不可避免的重要部分。无论是处理网络请求、读取文件,还是与数据库交互,我们都离不开异步操作。而 Promise,作为 ES6 引入的异步解决方案之一,在实际开发中发挥着核心作用。本文将深入探讨 JavaScript 中的 Promise API,帮助读者全面理解其工作原理、常用方法及应用场景。

一、Promise 是什么?

Promise 是 JavaScript 中用于处理异步操作的一种对象。它代表了某个异步操作最终可能完成(fulfilled)或失败(rejected)的一种值。

1.1 Promise 的定义

js 复制代码
const promise = new Promise((resolve, reject) => {
  // 异步操作
  if (/* 成功 */) {
    resolve(value); // 成功时调用
  } else {
    reject(error);  // 失败时调用
  }
});

1.2 Promise 的三种状态

Promise 有三种状态:

  • pending(进行中):初始状态,既没有被 fulfilled,也没有被 rejected。

  • fulfilled(已成功):操作成功完成。

  • rejected(已失败):操作失败。

一旦状态改变,就不可再更改,即 Promise 是不可逆的。

二、Promise 的基本用法

2.1 then() 方法

用于注册成功和失败的回调函数。

js 复制代码
promise
  .then((value) => {
    console.log("成功:", value);
  })
  .catch((error) => {
    console.error("失败:", error);
  });

2.2 catch() 方法

是 then(null, rejection) 的语法糖,用于捕获异常。

js 复制代码
promise.catch((error) => {
  console.error("发生错误:", error);
});

2.3 finally() 方法

不论 Promise 最终是成功还是失败,finally() 中的回调都会执行。

js 复制代码
promise
  .then((value) => {
    console.log("成功", value);
  })
  .catch((error) => {
    console.error("失败", error);
  })
  .finally(() => {
    console.log("无论成功或失败都执行");
  });

三、Promise 链式调用

Promise 最大的优点之一就是链式调用。你可以通过连续的 then() 方法来处理多个异步操作。

js 复制代码
fetchData()
  .then((data) => processData(data))
  .then((result) => renderData(result))
  .catch((error) => console.error(error));

这样写的好处是避免了"回调地狱"(callback hell),代码结构更清晰。

四、Promise 的静态方法

4.1 Promise.resolve()

返回一个状态为 fulfilled 的 Promise 对象。

js 复制代码
Promise.resolve("ok").then((res) => console.log(res)); // 输出 "ok"

4.2 Promise.reject()

返回一个状态为 rejected 的 Promise 对象。

js 复制代码
Promise.reject("error").catch((err) => console.error(err)); // 输出 "error"

4.3 Promise.all()

并发执行多个 Promise,并在全部成功后返回结果数组。

js 复制代码
const p1 = Promise.resolve(1);
const p2 = Promise.resolve(2);
const p3 = Promise.resolve(3);

Promise.all([p1, p2, p3]).then((values) => {
  console.log(values); // [1, 2, 3]
});

⚠️ 注意:只要有一个 Promise 失败,整个 Promise.all 就会失败。

4.4 Promise.race()

多个 Promise 中只要有一个改变状态(无论成功或失败),就立即返回该结果。

js 复制代码
Promise.race([
  new Promise((res) => setTimeout(() => res("快的"), 100)),
  new Promise((res) => setTimeout(() => res("慢的"), 200)),
]).then((res) => {
  console.log(res); // 输出 "快的"
});

4.5 Promise.allSettled()

与 Promise.all() 类似,但无论失败与否,都能返回每个 Promise 的最终状态。

js 复制代码
Promise.allSettled([
  Promise.resolve(1),
  Promise.reject("失败"),
]).then((results) => {
  console.log(results);
  // [
  //   { status: 'fulfilled', value: 1 },
  //   { status: 'rejected', reason: '失败' }
  // ]
});

4.6 Promise.any()

只要其中一个 Promise 成功,就返回成功的结果。若全部失败,则返回 AggregateError。

js 复制代码
Promise.any([
  Promise.reject("错误1"),
  Promise.resolve("成功的"),
  Promise.reject("错误2"),
]).then((value) => {
  console.log(value); // 成功的
});

五、实现一个简单的 Promise

为了深入理解 Promise 的原理,我们来模拟一个简单的 Promise 实现:

js 复制代码
class MyPromise {
  constructor(executor) {
    this.status = "pending";
    this.value = undefined;
    this.reason = undefined;

    this.onResolvedCallbacks = [];
    this.onRejectedCallbacks = [];

    const resolve = (value) => {
      if (this.status === "pending") {
        this.status = "fulfilled";
        this.value = value;
        this.onResolvedCallbacks.forEach((fn) => fn());
      }
    };

    const reject = (reason) => {
      if (this.status === "pending") {
        this.status = "rejected";
        this.reason = reason;
        this.onRejectedCallbacks.forEach((fn) => fn());
      }
    };

    try {
      executor(resolve, reject);
    } catch (err) {
      reject(err);
    }
  }

  then(onFulfilled, onRejected) {
    if (this.status === "fulfilled") {
      onFulfilled(this.value);
    }

    if (this.status === "rejected") {
      onRejected(this.reason);
    }

    if (this.status === "pending") {
      this.onResolvedCallbacks.push(() => onFulfilled(this.value));
      this.onRejectedCallbacks.push(() => onRejected(this.reason));
    }
  }
}


六、Promise 与 async/await
ES2017 引入了 async/await,它是基于 Promise 的语法糖,使异步代码更像同步代码,易于编写和维护。
async function fetchData() {
  try {
    const res = await fetch("https://api.example.com");
    const data = await res.json();
    console.log(data);
  } catch (err) {
    console.error(err);
  }
}

await 后面的表达式必须是一个 Promise。async 函数会自动返回一个 Promise。

七、Promise 的异常处理机制

Promise 具有链式的异常传递机制。如果 then 中抛出了错误,后续的 catch 会捕获它。

js 复制代码
Promise.resolve()
  .then(() => {
    throw new Error("出错了");
  })
  .catch((err) => {
    console.error("捕获异常:", err.message); // 捕获异常:出错了
  });

此外,如果在 catch 之后继续写 then,Promise 会恢复为成功状态。

js 复制代码
Promise.reject("失败")
  .catch((err) => {
    console.log("处理失败:", err);
    return "默认值";
  })
  .then((res) => {
    console.log("恢复执行:", res); // 恢复执行:默认值
  });

八、实际开发中的 Promise 应用

8.1 接口请求并发处理

js 复制代码
async function loadAllData() {
  const [userInfo, articles, comments] = await Promise.all([
    fetchUserInfo(),
    fetchArticles(),
    fetchComments(),
  ]);

  console.log(userInfo, articles, comments);
}

8.2 防抖节流后的 Promise 延时

js 复制代码
function delay(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

async function run() {
  console.log("开始");
  await delay(1000);
  console.log("1 秒后执行");
}

九、常见错误与注意事项

  • then 中不返回新的 Promise:容易造成逻辑中断。

  • 未处理 catch 异常:会导致 Promise "吞掉"异常。

  • 在循环中使用 Promise:要注意执行顺序和并发控制。

  • Promise 状态不可逆:resolve/reject 只能调用一次。

十、总结

  • Promise 是处理异步操作的强大工具,具有链式调用、错误捕获、并发处理等优势。

  • 它有三种状态:pending、fulfilled 和 rejected,一旦状态确定,就不可再更改。

  • 提供多个静态方法如 Promise.all, Promise.race, Promise.allSettled 等,满足不同需求。

  • async/await 是基于 Promise 的语法糖,更加简洁优雅。

  • 理解 Promise 的机制和链式调用,有助于编写更健壮、更清晰的异步代码。

如果你在实际项目中深入使用 Promise,一定会体会到它为异步编程带来的清晰结构与可维护性。在未来的开发中,配合 async/await,将进一步提升你的开发效率和代码质量。

欢迎关注我的公众号获取更多技术干货!

相关推荐
lzhdim3 小时前
C#不为人知的10个魔法特性:资深开发者也会震惊的底层奥秘
java·前端·javascript·算法·c#
zach4 小时前
前端落地实战:Next.js 项目 Nginx + PM2 生产部署全流程(附踩坑总结)
前端·pm2·next.js
overmind4 小时前
oeasy教h5前端001 修改的乐趣_直接修改在线网页
前端
只睡四小时4 小时前
JS 手写 D-pad 空间导航:电视端焦点引擎实战
android·开发语言·前端·javascript·ecmascript·hls·空间导航
恋猫de小郭4 小时前
Android 原生的 Compose A2UI 也来了,你还抱着 XML 养老吗?
android·前端·flutter
庄园特聘拆椅狂魔4 小时前
从连连看到粒子消散——前端小游戏与视觉特效的技术选型
前端
JudithHuang5 小时前
React 常用 Hooks
前端·react.js·前端框架
sycmancia6 小时前
Qml——Window元素使用
前端
每天都好困6 小时前
03 AR 投影与误差修正:把地图上的点贴到视频画面上
前端
每天都好困6 小时前
04 地图图层封装:像管理 Photoshop 图层一样管理地图元素
前端