当前位置: 首页 > backend >正文

现代 JavaScript (ES6+) 入门到实战(五):告别回调地狱,Promise 完全入门

到目前为止,我们处理的都是同步代码——代码从上到下一行行执行,简单直接。但 JavaScript 的世界中,大量操作都是异步的,比如网络请求(AJAX)、文件读取、定时器等。它们不会阻塞主线程,而是在未来的某个时刻才返回结果。

在 ES6 之前,我们处理异步的唯一方式就是回调函数。当一个异步操作需要依赖另一个的结果时,灾难就降临了。

一、回忆杀:噩梦般的回调地狱 (Callback Hell)

假设我们需要依次请求三个接口:获取用户信息 -> 根据用户信息获取其帖子列表 -> 根据帖子 ID 获取评论。

【过去我们这么写 (ES5 - 以 jQuery Ajax 为例)】

$.ajax({url: 'api/user/1',success: function(user) {console.log('第一步:获取到用户', user.name);// 成功后,发起第二个请求$.ajax({url: 'api/posts/' + user.id,success: function(posts) {console.log('第二步:获取到帖子', posts.length, '篇');// 成功后,发起第三个请求$.ajax({url: 'api/comments/' + posts[0].id,success: function(comments) {console.log('第三步:获取到评论');// 如果还有第四步、第五步...},error: function(err) {console.error('获取评论失败', err);}});},error: function(err) {console.error('获取帖子失败', err);}});},error: function(err) {console.error('获取用户失败', err);}
});

这种向右无限延伸的“金字塔”结构,就是臭名昭著的“回调地狱”。它有三大罪状:

  1. 可读性极差:代码逻辑混乱,难以理解。
  2. 难以维护:修改其中一个环节,可能导致整个逻辑链的崩溃。
  3. 错误处理分散:每个异步操作都需要单独处理错误,代码冗余。

为了将开发者从地狱中解救出来,ES6 带来了官方的异步解决方案——Promise

二、进化时刻:Promise 登场

Promise 的中文意思是“承诺”。你可以把它想象成一张“承诺回执单”。

当你执行一个异步操作时,它不会立即给你结果,而是先给你一张“回执单”(即 Promise 对象)。这张单子向你承诺:“我将来一定会给你一个结果,要么是成功的结果,要么是失败的原因。”

一个 Promise 对象有三种状态:

  1. Pending (进行中):初始状态,承诺还在兑现中。
  2. Fulfilled (已成功):操作成功完成,承诺已兑现。
  3. Rejected (已失败):操作失败,承诺被拒绝。

这个状态是单向的,一旦从 pending 变为 fulfilledrejected,就再也不会改变。

三、如何使用 Promise?

现代的异步 API(如 fetch)天生就返回 Promise。我们先来学习如何“消费”一个 Promise。

1. .then() - 接收成功的结果
.then() 方法用于指定当 Promise 状态变为 fulfilled (成功) 时,应该执行什么操作。

2. .catch() - 捕获失败的原因
.catch() 方法用于指定当 Promise 状态变为 rejected (失败) 时,应该执行什么操作。

【现在我们这么写 (ES6+ - 以 fetch 为例)】
fetch 是浏览器内置的、基于 Promise 的网络请求 API。

fetch('api/user/1') // fetch 返回一个 Promise.then(response => {// 第一个 .then 处理 HTTP 响应if (!response.ok) {throw new Error('网络响应错误');}return response.json(); // .json() 也返回一个 Promise}).then(user => {// 第二个 .then 处理 JSON 数据console.log('成功获取到用户:', user);}).catch(error => {// 统一处理所有错误console.error('请求过程中发生错误:', error);});

四、Promise 的真正威力:链式调用

Promise 最强大的地方在于,它将“回调地狱”拉平成了一条直线,形成优雅的链式调用。

.then().catch() 方法执行后,它自身也会返回一个新的 Promise 对象,这使得我们可以像工厂流水线一样,将一系列异步操作串联起来。

现在,我们用 Promise 来重构开头的那个“地狱”:
(假设我们已经把 $.ajax 封装成返回 Promise 的函数 ajaxPromise)

【现在我们这么写 (ES6+)】

ajaxPromise('api/user/1').then(user => {console.log('第一步:获取到用户', user.name);// 返回一个新的 Promise,交给下一个 .then 处理return ajaxPromise('api/posts/' + user.id);}).then(posts => {console.log('第二步:获取到帖子', posts.length, '篇');return ajaxPromise('api/comments/'' + posts[0].id);}).then(comments => {console.log('第三步:获取到评论');}).catch(err => {// 统一的错误处理!// 链条中任何一个环节出错,都会被这个 catch 捕获console.error('链式调用中发生错误:', err);});

看到了吗?原本横向发展的“金字塔”,现在变成了纵向发展的“流水线”。代码结构清晰,逻辑一目了然,并且错误处理也变得非常简单、集中。

总结

  • Promise 是处理异步操作的对象,它代表一个未来的结果。
  • 它通过链式调用,完美地解决了回调地狱问题。
  • 使用 .then() 处理成功,使用 .catch() 统一处理失败。
  • .then() 中可以返回一个新的 Promise,从而将异步操作串联起来。

Promise 的出现,是 JavaScript 异步编程史上的一座里程碑。但进化并未停止。ES7 带来了 async/await,它能让异步代码看起来就像同步代码一样简洁。

在下一篇,我们将迎来异步编程的终极形态!敬请期待!

http://www.xdnf.cn/news/14773.html

相关文章:

  • 免费SSL证书一键申请与自动续期
  • STM32——HAL库总结
  • 【AGI】Qwen VLo:多模态AI的范式重构与AGI演进关键里程碑
  • mac触摸板设置右键
  • 【HuggingFace】模型下载至本地访问
  • 基于Pandas和FineBI的昆明职位数据分析与可视化实现(三)- 职位数据统计分析
  • 条件概率:不确定性决策的基石
  • C#写破解rar文件密码例程
  • 【硬核数学】10. “价值标尺”-损失函数:信息论如何设计深度学习的损失函数《从零构建机器学习、深度学习到LLM的数学认知》
  • Android大图加载优化:BitmapRegionDecoder深度解析与实战
  • IDE/IoT/实践小熊派LiteOS工程配置、编译、烧录、调试(基于 bearpi-iot_std_liteos 源码)
  • 马斯克的 Neuralink:当意念突破肉体的边界,未来已来
  • 同步日志系统深度解析【链式调用】【宏定义】【固定缓冲区】【线程局部存储】【RAII】
  • 《汇编语言:基于X86处理器》第5章 过程(2)
  • C# 委托(为委托添加方法和从委托移除方法)
  • 暑假复习篇之类与对象
  • gantt-task-react的改造使用
  • 源码运行效果图(六)
  • cocos creator 3.8 - 精品源码 - 六边形消消乐(六边形叠叠乐、六边形堆叠战士)
  • 《自动控制原理 》- 第 1 章 自动控制的基本原理与方式
  • 计算机操作系统(十七)内存管理
  • OpenCV图像噪点消除五大滤波方法
  • 能否仅用两台服务器实现集群的高可用性??
  • 创建套接字时和填充地址时指定类型的异同
  • 【LeetCode 热题 100】438. 找到字符串中所有字母异位词——(解法三)不定长滑动窗口+数组
  • 使用docker编译onlyoffice server 8.2.2 成功版 含踩坑记录
  • C++ STL深度剖析:Stack、queue、deque容器适配器核心接口
  • FDA IND审评流程及临床研究暂停要点
  • Ubuntu20.04离线安装Realtek b852无线网卡驱动
  • Java基础(Maven配置)