一篇梳理清楚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 成为构建高效、可维护异步逻辑的基石。

相关推荐
C澒9 小时前
前端技术核心领域与实践方向
前端·系统架构
Remember_9939 小时前
Java 单例模式深度解析:设计原理、实现范式与企业级应用场景
java·开发语言·javascript·单例模式·ecmascript
写代码的【黑咖啡】9 小时前
Python 中的自然语言处理利器:NLTK
前端·javascript·easyui
Swift社区9 小时前
Nginx 反向代理配置 React 前端与 Python 后端
前端·nginx·react.js
可问春风_ren9 小时前
Vue3 入门详解:从基础到实战
开发语言·前端·javascript·vue.js·前端框架·ecmascript·edge浏览器
摘星编程10 小时前
用React Native开发OpenHarmony应用:NativeStack原生导航
javascript·react native·react.js
一起养小猫10 小时前
Flutter for OpenHarmony 实战:从零开发一款五子棋游戏
android·前端·javascript·flutter·游戏·harmonyos
晚霞的不甘10 小时前
Flutter for OpenHarmony全面升级「今日运势」 应用的视觉与交互革新
前端·学习·flutter·前端框架·交互
学嵌入式的小杨同学10 小时前
【Linux 封神之路】文件操作 + 时间编程实战:从缓冲区到时间格式化全解析
linux·c语言·开发语言·前端·数据库·算法·ux
RFCEO10 小时前
学习前端编程:精准选中 HTML 元素的技巧与方法
前端·学习·css类选择器·兄弟元素选中·父子选中·关系选中·选择器选中