一篇梳理清楚JavaScript ES6中的Promise

ops/2024/12/27 10:47:49/

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


http://www.ppmy.cn/ops/144917.html

相关文章

Linux系统文件

/etc初始化系统重要⽂件 /etc/sysconfig/network-scripts/ifcfg-eth0:⽹卡配置⽂件 /etc/resolv.conf:Linux系统DNS客户端配置⽂件 /etc/hostname (CentOS7) /etc/sysconfig/network:(CentOS 6)主机名配置⽂件 /etc/hosts:系统本地的DNS解析⽂件 /etc/fstab:配置开机设备⾃动挂…

sqlite基础

在 SQLite 中,可以使用 CREATE INDEX 语句为表中的字段添加索引,以加速查询操作。 1. 为单个字段添加索引 假设有一个表 users,并且你想为 email 字段创建索引: CREATE INDEX idx_users_email ON users(email);这条语句会为 us…

如何编写 Prompt

如何编写 Prompt Prompt 示例参考 对于特定的任务来说,没有万能的Prompt,只有一些通用的模式,要完成这个任务还需要这个任务特定的 Example,大部分优秀的 Prompt 都需要 Example,这其实应用了模型的短期学习能力。另外…

计算机基础知识复习12.24

http和https有那些区别 http是超文本传输协议,信息是明文传输,存在安全风险的问题,https则解决http不安全的缺点,在TCP和HTTP网络层之间加入了SSL/TLS安全协议,使得报文能够加密传输 http连接建立相对简单&#xff0…

windows 钉钉缓存路径不能修改 默认C盘解决方案

1.问题背景 windows系统C盘被钉钉缓存占用大量空间,导致C盘存储严重不足;但钉钉不支持修改缓存路径 2.解决方案 为钉钉缓存路径创建软连接到其他目录 3.解决步骤 a.钉钉设置里找到,钉钉缓存路径 C:\Users\admin\AppData\Roaming\DingTalk …

SSM书影音社区前端设计与实现u15u5--(程序+源码+数据库+调试部署+开发环境)

本系统****(程序源码数据库调试部署开发环境)带论文文档1万字以上,文末可获取,系统界面在最后面。**** 系统程序文件列表 开题报告内容 一、项目背景 随着互联网的普及和数字化时代的到来,人们获取信息和娱乐的方式…

Vscode GStreamer插件开发环境配置

概述 本教程使用vscode和Docker搭建Gstreamer2.24的开发环境,可以用于开发调试Gstreamer程序或者自定义插件开发。 1. vscode依赖插件 C/C Extension Pack(ms-vscode.cpptools-extension-pack):该插件包包含一组用于 Visual St…

NGINX常用编译参数

过滤IP组件: –add-module/opt/ngx_http_geoip2_module-3.4 下载地址:ngx_http_geoip2_module 控制heade组件: –add-module/opt/headers-more-nginx-module-0.37 下载地址:headers-more-nginx-module ./configure \--prefix/…