一篇梳理清楚JavaScript ES6中的Promise

1. 历史背景

在 JavaScript 的早期,异步操作主要通过回调函数(callback)实现。然而,随着应用变得更加复杂,嵌套的回调函数逐渐导致了 "回调地狱"(callback hell) 的问题。例如:

javascript 复制代码
readFile('file1.txt', (err, data) => {
  if (err) {
    console.error(err);
  } else {
    processFile(data, (err, result) => {
      if (err) {
        console.error(err);
      } else {
        saveFile('file2.txt', result, (err) => {
          if (err) console.error(err);
        });
      }
    });
  }
});

这种代码既难以阅读,又难以维护。为了缓解这一问题,ES6(2015年)引入了 Promise,一种用于更优雅地处理异步操作的机制。


2. 什么是 Promise?

Promise 是一个表示异步操作最终完成或失败的对象,其状态会经历以下三种状态之一:

  1. Pending(等待):初始状态,既没有完成也没有失败。
  2. Fulfilled(已完成):操作成功完成,Promise 变为 resolved 状态。
  3. Rejected(已拒绝):操作失败,Promise 变为 rejected 状态。

Promise 的状态一旦从 Pending 转变为 Fulfilled 或 Rejected,就不可再改变。


3. 基本使用

3.1 创建一个 Promise

Promise 构造函数接收一个执行器函数(executor),该函数包含两个参数 resolvereject

javascript 复制代码
const myPromise = new Promise((resolve, reject) => {
  let success = true;

  if (success) {
    resolve("Operation succeeded!");
  } else {
    reject("Operation failed!");
  }
});

3.2 使用 .then().catch()

javascript 复制代码
myPromise
  .then((result) => {
    console.log(result); // 输出: Operation succeeded!
  })
  .catch((error) => {
    console.error(error);
  });

3.3 使用 .finally()

.finally() 方法在 Promise 结束后总会执行,无论是成功还是失败。

javascript 复制代码
myPromise
  .then((result) => console.log(result))
  .catch((error) => console.error(error))
  .finally(() => console.log("Operation finished."));

4. Promise 的链式调用

通过 .then() 链式调用,避免回调地狱:

javascript 复制代码
const fetchData = (url) => {
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(`Data from ${url}`);
    }, 1000);
  });
};

fetchData('api/data1')
  .then((data1) => {
    console.log(data1);
    return fetchData('api/data2');
  })
  .then((data2) => {
    console.log(data2);
    return fetchData('api/data3');
  })
  .then((data3) => {
    console.log(data3);
  })
  .catch((error) => {
    console.error("Error:", error);
  });

5. 常用的 Promise 静态方法

5.1 Promise.all

用于等待多个 Promise 并行完成。如果任一 Promise 失败,则返回的 Promise 被拒绝。

javascript 复制代码
Promise.all([
  fetchData('api/data1'),
  fetchData('api/data2'),
  fetchData('api/data3')
])
  .then((results) => {
    console.log(results); // ['Data from api/data1', 'Data from api/data2', 'Data from api/data3']
  })
  .catch((error) => {
    console.error("Error:", error);
  });

5.2 Promise.race

用于返回第一个完成(无论成功或失败)的 Promise。

javascript 复制代码
Promise.race([
  fetchData('api/fast'),
  fetchData('api/slow')
])
  .then((result) => {
    console.log(result); // 可能是 'Data from api/fast'
  });

5.3 Promise.allSettled

即使部分 Promise 失败,也会等待所有 Promise 完成。

javascript 复制代码
Promise.allSettled([
  fetchData('api/data1'),
  Promise.reject("Failed operation"),
])
  .then((results) => {
    console.log(results);
    // [
    //   { status: "fulfilled", value: "Data from api/data1" },
    //   { status: "rejected", reason: "Failed operation" }
    // ]
  });

5.4 Promise.any

只要有一个 Promise 成功,就返回其结果;如果所有 Promise 都失败,则返回一个包含失败原因的 AggregateError。

javascript 复制代码
Promise.any([
  Promise.reject("Error 1"),
  fetchData('api/data2'),
  Promise.reject("Error 2"),
])
  .then((result) => {
    console.log(result); // "Data from api/data2"
  })
  .catch((error) => {
    console.error(error);
  });

6. 注意事项和最佳实践

6.1 错误处理

始终使用 .catch() 捕获错误,以避免未处理的 Rejection:

javascript 复制代码
fetchData('invalid/url')
  .then((result) => console.log(result))
  .catch((error) => console.error("Error:", error));

6.2 避免嵌套 Promise

即使使用 Promise,也可能由于不当设计而引发嵌套问题:

javascript 复制代码
// 不推荐
fetchData('api/data1').then((data1) => {
  fetchData('api/data2').then((data2) => {
    console.log(data1, data2);
  });
});

// 推荐
fetchData('api/data1')
  .then((data1) => {
    return fetchData('api/data2').then((data2) => [data1, data2]);
  })
  .then(([data1, data2]) => {
    console.log(data1, data2);
  });

6.3 注意 Promise 状态不可逆

一旦状态从 Pending 变为 Fulfilled 或 Rejected,无法再次更改。这有助于避免状态冲突。

6.4 使用 async/await 简化代码

async/await 是 Promise 的语法糖,可以让异步代码看起来更像同步代码:

javascript 复制代码
const fetchSequentialData = async () => {
  try {
    const data1 = await fetchData('api/data1');
    console.log(data1);
    const data2 = await fetchData('api/data2');
    console.log(data2);
  } catch (error) {
    console.error("Error:", error);
  }
};

fetchSequentialData();

7. 总结

Promise 改变了 JavaScript 的异步编程方式,提供了更清晰的语义、更优雅的代码结构和更强大的错误处理能力。在现代开发中,与 async/await 结合使用,Promise 成为构建高效、可维护异步逻辑的基石。

相关推荐
腾讯TNTWeb前端团队6 小时前
helux v5 发布了,像pinia一样优雅地管理你的react状态吧
前端·javascript·react.js
范文杰9 小时前
AI 时代如何更高效开发前端组件?21st.dev 给了一种答案
前端·ai编程
拉不动的猪9 小时前
刷刷题50(常见的js数据通信与渲染问题)
前端·javascript·面试
拉不动的猪10 小时前
JS多线程Webworks中的几种实战场景演示
前端·javascript·面试
FreeCultureBoy10 小时前
macOS 命令行 原生挂载 webdav 方法
前端
uhakadotcom11 小时前
Astro 框架:快速构建内容驱动型网站的利器
前端·javascript·面试
uhakadotcom11 小时前
了解Nest.js和Next.js:如何选择合适的框架
前端·javascript·面试
uhakadotcom11 小时前
React与Next.js:基础知识及应用场景
前端·面试·github
uhakadotcom11 小时前
Remix 框架:性能与易用性的完美结合
前端·javascript·面试
uhakadotcom11 小时前
Node.js 包管理器:npm vs pnpm
前端·javascript·面试