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

深入理解ES6 Promise

一、为什么需要 Promise?

在 JavaScript 中,异步操作(如网络请求、文件读取、定时器)通常通过回调函数处理。但多个异步操作依赖或串行时,会出现回调地狱

// 回调地狱示例setTimeout(()=>{console.log('第一步');setTimeout(()=>{console.log('第二步');setTimeout(()=>{console.log('第三步');},1000);},1000);},1000);

回调地狱的缺点:

  • 代码横向增长,可读性差
  • 难以捕获错误
  • 难以并行或竞速处理多个异步任务

Promise 正是为了解决这些问题而诞生的,它让异步代码更接近同步写法的风格,且提供了统一的错误处理机制。


二、Promise 是什么?

Promise 是 ES6 新增的一个内置构造函数,代表一个异步操作的最终完成(或失败)及其结果值

字面理解:Promise 是一个“承诺”,承诺在未来某个时刻会给你一个结果(成功或失败)。

一个 Promise 对象处于以下三种状态之一:

状态含义特点
pending初始状态,未完成也未拒绝可以转变为fulfilledrejected
fulfilled操作成功完成有一个value(结果值)
rejected操作失败有一个reason(失败原因)

状态一旦改变,就永久固定,不会再变。


三、Promise 的基本用法

1. 创建一个 Promise

使用new Promise(executor),其中executor是一个立即执行的函数,它接收两个参数:resolvereject

constpromise=newPromise((resolve,reject)=>{// 异步操作constsuccess=true;setTimeout(()=>{if(success){resolve('成功的数据');// 将状态改为 fulfilled}else{reject('失败的原因');// 将状态改为 rejected}},1000);});

2. 使用.then().catch()消费 Promise

promise.then((value)=>{console.log('成功:',value);}).catch((error)=>{console.error('失败:',error);});
  • .then()接收两个可选参数:onFulfilledonRejected。通常只传第一个,错误交给.catch()
  • .catch().then(null, onRejected)的语法糖。

3..finally()—— 无论成功失败都会执行

promise.then(value=>console.log(value)).catch(error=>console.error(error)).finally(()=>console.log('操作结束,清理资源'));

四、Promise 的链式调用(核心特性)

.then().catch()返回一个新的 Promise,因此可以串联多个异步操作。

newPromise((resolve)=>{resolve(1);}).then((value)=>{console.log(value);// 1returnvalue+1;}).then((value)=>{console.log(value);// 2returnvalue+1;}).then((value)=>{console.log(value);// 3});

链式调用中的返回值处理规则:

  • 如果回调函数返回一个普通值(包括undefined),它会自动被包装成一个fulfilled的 Promise。
  • 如果返回一个 Promise,那么下一个.then()会等待这个 Promise 的结果。
  • 如果抛出异常throw new Error()),会进入下一个.catch()

示例:模拟异步任务顺序执行

functionasyncTask(name,duration){returnnewPromise((resolve)=>{setTimeout(()=>{console.log(`${name}完成`);resolve(name);},duration);});}asyncTask('任务1',1000).then(()=>asyncTask('任务2',500)).then(()=>asyncTask('任务3',800)).then(()=>console.log('所有任务完成'));

五、Promise 的静态方法

1.Promise.resolve(value)

快速返回一个状态为fulfilled的 Promise。

Promise.resolve('直接成功').then(console.log);// 直接成功

如果value本身是一个 Promise,则直接返回该 Promise。

2.Promise.reject(reason)

快速返回一个状态为rejected的 Promise。

Promise.reject('出错了').catch(console.error);// 出错了

3.Promise.all(iterable)

并行执行多个 Promise,所有都成功才成功,结果按顺序组成数组;任何一个失败则立即失败,并返回第一个失败的原因。

constp1=Promise.resolve(1);constp2=Promise.resolve(2);constp3=newPromise((resolve)=>setTimeout(resolve,1000,3));Promise.all([p1,p2,p3]).then((results)=>{console.log(results);// [1, 2, 3] (等待 1 秒后输出)});

失败示例:

constp1=Promise.resolve(1);constp2=Promise.reject('错误');Promise.all([p1,p2]).catch((err)=>{console.log(err);// '错误'});

4.Promise.race(iterable)

竞速:返回最先 settled(成功或失败)的那个 Promise 的结果

constp1=newPromise((resolve)=>setTimeout(resolve,500,'慢'));constp2=newPromise((resolve)=>setTimeout(resolve,100,'快'));Promise.race([p1,p2]).then(console.log);// '快'

常用于设置超时:

functionfetchWithTimeout(url,timeout=5000){constfetchPromise=fetch(url);consttimeoutPromise=newPromise((_,reject)=>setTimeout(()=>reject(newError('请求超时')),timeout));returnPromise.race([fetchPromise,timeoutPromise]);}

5.Promise.allSettled(iterable)(ES2020)

等待所有 Promise 都 settle(成功或失败),返回每个对象的状态和结果(或原因)。永远不会进入.catch()

constpromises=[Promise.resolve('成功'),Promise.reject('失败'),Promise.resolve('另一个成功'),];Promise.allSettled(promises).then((results)=>{console.log(results);/* [ { status: 'fulfilled', value: '成功' }, { status: 'rejected', reason: '失败' }, { status: 'fulfilled', value: '另一个成功' } ] */});

6.Promise.any(iterable)(ES2021)

只要有一个 Promise 成功就返回那个成功的结果;如果所有都失败,则返回一个AggregateError(聚合错误)。

constp1=Promise.reject('错误1');constp2=Promise.reject('错误2');constp3=Promise.resolve('成功');Promise.any([p1,p2,p3]).then(console.log);// '成功'

全部失败的情况:

Promise.any([Promise.reject('a'),Promise.reject('b')]).catch((err)=>{console.log(errinstanceofAggregateError);// trueconsole.log(err.errors);// ['a', 'b']});

六、错误处理的最佳实践

  1. 总是添加.catch()或者在每个.then()的第二个参数中处理错误,否则错误会被静默吞没(虽然浏览器会报 unhandled rejection,但不推荐依赖于此)。

  2. 在链式调用的末尾统一捕获错误

doSomething().then(doSomethingElse).then(doThirdThing).catch(handleError);// 捕获前面任意一步的错误
  1. 不要在.then()中抛出普通字符串,而应该抛出Error对象,以便获得堆栈信息。

  2. async/await与 try-catch是 Promise 的语法糖,但底层依然是 Promise,理解 Promise 是掌握async/await的基础。


七、Promise 与回调地狱对比

回调地狱

getUser((user)=>{getPosts(user.id,(posts)=>{getComments(posts[0].id,(comments)=>{console.log(comments);});});});

Promise 版本(假设每个函数都返回 Promise):

getUser().then(user=>getPosts(user.id)).then(posts=>getComments(posts[0].id)).then(comments=>console.log(comments)).catch(error=>console.error(error));

扁平化,更易读。


八、进阶示例:串行与并行控制

1. 串行执行一组异步任务(逐个进行)

consttasks=[task1,task2,task3];// 每个 task 返回 Promisetasks.reduce((prevPromise,task)=>{returnprevPromise.then(()=>task());},Promise.resolve());

2. 限制并发数量(例如同时最多 3 个请求)

asyncfunctionlimitConcurrent(tasks,limit){constresults=[];constexecuting=[];for(consttaskoftasks){constp=task().then(r=>results.push(r));executing.push(p);if(executing.length>=limit){awaitPromise.race(executing);// 清除已完成的任务executing.splice(executing.findIndex(e=>e===p),1);}}awaitPromise.all(executing);returnresults;}

九、Promise 的局限性

问题说明
不可取消一旦创建 Promise,无法从外部取消它的执行(某些库提供 cancelable promise,但非标准)。
无法获取中间状态无法同步获取“进度”,只能拿到最终结果(不过可以自己实现进度通知)。
错误可能被吞没若没有.catch()且未被async/await捕获,错误将静默消失(现代浏览器会打印 unhandledrejection 警告)。
新手容易误解例如忘记return导致链断裂,或在.then()中创建新的 Promise 但未返回。

十、总结

  • Promise 是异步编程的基石,它将回调函数的嵌套转化为链式调用。
  • 核心掌握:状态(pending/fulfilled/rejected)、链式调用静态方法allraceallSettledany)。
  • 搭配async/await使用时,Promise 依然在幕后工作,理解 Promise 有助于写出更可靠的异步代码。
  • 实际开发中,几乎所有现代 JavaScript API(如fetchfs.promisessetTimeout包装)都基于 Promise。
http://www.cnnetsun.cn/news/1870260.html

相关文章:

  • Python电子书处理终极指南:如何用EbookLib轻松管理EPUB文件
  • STM32H743双CAN总线负载太高?试试用CubeIDE+CanFestival同时跑两个CANopen主站
  • 深度解析Unity资源管理:YooAsset 2.2.12跨平台加密方案完全指南
  • BiliTools:3步解锁哔哩哔哩高效学习新体验,让知识获取速度提升300%
  • 5个简单步骤:使用Campus-Imaotai实现茅台自动预约的完整指南
  • Redis 高级篇(最佳实践)
  • 解锁博士论文“超能力”:好写作AI,学术征途的“超级外挂”
  • 把 ABAP RFC Gateway 日志真正配明白,SMGW、gw/logging 与 secinfo、reginfo 的实战思路
  • 【自动驾驶】从轨迹规划到安全评估:核心术语场景化解读
  • CoPaw驱动智能RPA:通过自然语言指令自动化办公流程
  • Windows 11系统优化指南:用Win11Debloat释放40%系统性能
  • 终极指南:如何绕过Windows驱动签名强制验证(DSEFix完整教程)
  • SystemVerilog--- task---高效调试与验证技巧
  • DeepSeek-R1-Distill-Qwen-1.5B实战:低配电脑也能流畅运行的代码助手
  • 图图的嗨丝造相-Z-Image-Turbo多场景落地:动漫同人图/轻小说插画/游戏立绘
  • 003、YOLO初探:目标检测核心思想与YOLO系列模型演进史
  • Self-Reflection如何提升Agent输出质量
  • 芯片设计新手必看:三大定律背后的实战避坑指南(附最新行业趋势)
  • Java浏览器自动化的终极革命:Jvppeteer深度解析与实践指南
  • ROS串口通讯实战:从蓝牙设备读取并解析数据
  • 终极指南:Windows系统快速安装苹果USB和移动设备以太网驱动
  • 模块化架构引擎:重构英雄联盟客户端工具集的技术实现
  • 专业VMP脱壳工具:如何高效实现VMProtect逆向分析与修复
  • 智能家居监控——基于STM32与ESP8266-01S的DHT11温湿度数据实时上传至阿里云物联网平台(一)
  • 机器学习与深度学习的区别全面对比:什么区别如何选择研究方向区别
  • 良心推荐:零基础学深度学习需要学哪些框架?PyTorch 和 TensorFlow 选哪个?
  • 测试工程师的“大家来找茬”职业病,在生活中有多可怕?
  • 2025最权威的六大AI学术助手解析与推荐
  • 从产品经理视角看技术实现:拆解‘苍穹外卖’套餐管理的业务逻辑与接口设计
  • 别再用FGSM了!AIAgent架构中必须升级的5代对抗训练范式,实测对抗准确率从61.2%跃升至94.8%