Promise简介和使用
文章目录
- 一、异步和同步编程简介
- 二、Promise
- 1.Promise简介
- 2.特点
- 3. 基本用法
一、异步和同步编程简介
- 同步编程:代码从上到下顺序执行,上面的代码不执行完毕,下面的代码不执行
- 异步编程:代码执行的时候,遇见异步代码,不会等待,继续执行下面同步代码,同步代码执行完毕后;再去执行异步代码
异步操作
- 事件
- ajax事件
- 定时器
- 为什么引入同步和异步
同步
顺序执行:代码按照书写顺序逐行执行,前一个任务完成后,才能执行下一个任务。
阻塞性:如果某个任务耗时较长(如文件读写、网络请求),后续代码必须等待它完成才能继续执行。
异步
非阻塞执行:任务发起后,程序继续执行后续代码,无需等待耗时操作完成。
并行处理:通过回调(Callbacks)、Promise、事件循环(Event Loop)等机制,在后台处理耗时任务,完成后通知主程序。
引入同步和异步机制的核心原因
在于平衡执行效率、资源利用率与代码复杂性之间的矛盾,以满足不同场景下的系统需求 - 异步的弊端
- 异步编程获取结果的方式和同步编程不一样,
同步编程:可以基于函数的返回值获取结果,
异步编程:需要通过回调函数的方式获取结果。 - 基于回调方式去处理异步问题,会出现回调嵌套,一般也叫回调地狱,不利于后期的维护和阅读
- 异步编程获取结果的方式和同步编程不一样,
二、Promise
Promise解决回调地狱的问题
Promise是原生的对象
1.Promise简介
Promise是ES6提出一种异步解决方案, 比传统的解决方案——回调函数和事件——更合理和更强大。它由社区最早提出和实现,ES6 将其写进了语言标准,统一了用法,原生提供了Promise对象。
Promise 是一个对象,从它可以获取异步操作的消息。Promise 提供统一的 API,各种异步操作都可以用同样的方法进行处理。
2.特点
- 对象的状态不受外界影响
Promise 是一个容器对象,保存未来某个异步操作的结果(成功或失败),并通过统一 API 管理这些操作Pending(进行中):初始状态,异步操作未完成。
Fulfilled(已成功):异步操作成功完成,返回结果值。
Rejected(已失败):异步操作失败,返回错误原因
状态一旦从 Pending 变为 Fulfilled 或 Rejected,则不可逆 - 一旦状态改变,就不会再变,任何时候都可以得到这个结果
Promise对象的状态改变从pending变为fulfilled
从pending变为rejected
有了Promise对象,就可以将异步操作以同步操作的流程表达出来,避免了层层嵌套的回调函数。此外,Promise对象提供统一的接口,使得控制异步操作更加容易。 - 缺点
无法取消Promise,一旦新建会立即执行,无法中途取消。
如果不设置回调函数,Promise内部抛出的错误,不会反应到外部。
当处于pending状态时,无法得知目前进展到哪一个阶段(刚刚开始还是即将完成)。
3. 基本用法
- 创建Promise
通过构造函数传入执行器函数(Executor),内部调用 resolve 或 reject 改变状态const promise = new Promise((resolve, reject) => {// 可以同步或者异步代码, 一般写异步代码setTimeout(() => {Math.random() > 0.5 ? resolve("成功") : reject("失败");}, 1000);});
- 响应结果
.then():处理成功状态(Fulfilled)的结果。
.catch():处理失败状态(Rejected)的错误。
.finally():无论成功或失败均执行清理操作,用于指定不管 Promise 对象最后状态如何,都会执行的操作。该方法是 ES2018 引入标准的。 - 链式调用
const fs = require('fs')function read(path) {return new Promise((resolve, reject) => {fs.readFile(path, 'utf-8', (error, data) => {if (error) {reject(error)} else {resolve(data)}})}) } let ap = read('./a.txt').then((data) => {console.log('a.txt', data);return read('./b.txt') }, error => {console.log(error); }).then((data) => {console.log('b.txt',data);return read('./c.txt') }, error => {console.log(error); }) .then((data) => {console.log('c.txt',data); }, error => {console.log(error); })
- Promise.all()
并行执行多个 Promise,全部成功时返回结果数组,任一失败则立即终止
Promise.all()方法用于将多个 Promise 实例,包装成一个新的 Promise 实例const p1 = Promise.resolve(1); const p2 = Promise.resolve(2); const p3 = Promise.resolve(3);Promise.all([p1, p2, p3]).then(results => console.log(results));
- 状态改变:
- 全部成功
仅当所有输入的 Promise 均变为 fulfilled 时,返回的 Promise p 才会变为 fulfilled,且结果按顺序组成数组传递给 .then() - 任一失败
若任意一个输入的 Promise 变为 rejected,则 p 立即变为 rejected,并将 第一个被拒绝的原因 传递给 .catch()
- 全部成功
- 错误处理机制:
未捕获的拒绝
若输入的 Promise 未显式定义 .catch() 且被拒绝,Promise.all() 会直接终止执行,后续操作不会触发Promise.all([p1, p2.reject(), p3]).finally(() => console.log("不会执行"));
推荐做法
为每个输入的 Promise 显式处理错误(如 .catch()),避免静默失败
- Promise.race()
返回最先完成的 Promise(无论成功或失败)
Promise.race()方法同样是将多个 Promise 实例,包装成一个新的 Promise 实例。成功优先:若第一个完成的 Promise 是 fulfilled,p 会直接成功。
失败优先:若第一个完成的 Promise 是 rejected,p 会直接失败。// 失败优先 const p1 = new Promise(resolve => setTimeout(resolve, 100, "成功1")); const p2 = new Promise(reject => setTimeout(reject, 200, "失败2")); const p3 = new Promise(reject => setTimeout(reject, 50, "失败3"));Promise.race([p1, p2, p3]).then(result => console.log(result)) // 输出 "失败3"(p3 先完成).catch(error => console.error(error));// 成功优先 const p4 = new Promise(reject => setTimeout(reject, 150, "失败4")); const p5 = new Promise(resolve => setTimeout(resolve, 100, "成功5")); const p6 = new Promise(resolve => setTimeout(resolve, 50, "成功6")); Promise.race([p4, p5, p6]).then(result => console.log(result)).catch(error => console.error(error)); // 输出 "成功6"(p6 先完成)
- 超时控制:异步操作限时执行
通过 Promise.race() 实现 超时中断逻辑,提升程序的健壮性。// 模拟异步任务(如 API 请求) function asyncTask() {return new Promise(resolve => setTimeout(resolve, 3000, "任务完成")); }// 超时控制(2秒内未完成则终止) function timeout(ms) {return new Promise((_, reject) => setTimeout(reject, ms, "超时!")); }Promise.race([asyncTask(), timeout(2000)]).then(result => console.log(result)) .catch(error => console.error(error)); // 2秒后输出 "超时!"
- 竞速请求:获取最快的响应结果
从多个数据源(如 CDN 节点)发起并行请求,优先使用最先返回的结果。// 模拟不同延迟的 API 请求 const api1 = fetch("https://api1.example.com").then(res => res.json()); const api2 = fetch("https://api2.example.com").then(res => res.json()); const api3 = fetch("https://api3.example.com").then(res => res.json());Promise.race([api1, api2, api3]).then(data => console.log("最快响应:", data)).catch(error => console.error("请求全部失败"));
- 错误隔离:避免竞速中的全局失败
若需在某个 Promise 失败后仍处理其他结果,需显式捕获单个错误。const p1 = new Promise(resolve => setTimeout(resolve, 100, "成功")); const p2 = new Promise((_, reject) => setTimeout(reject, 50, "失败"));// 为每个 Promise 添加错误处理 const safeRace = Promise.race([p1.catch(error => ({ type: "error", error })),p2.catch(error => ({ type: "error", error })) ]);safeRace.then(result => {if (result.type === "error") console.error("捕获到错误:", result.error);else console.log("成功结果:", result); }); // 输出 "捕获到错误:失败"(p2 先完成,但错误已被隔离)
- 注意事项
未完成的 Promise 仍会继续执行(需手动取消或忽略);
错误需显式处理,避免静默失败;
结果的顺序与输入顺序无关,仅依赖完成速度。
- Promise.allSettled()
等待所有 Promise 完成,返回包含每个结果状态和值的数组
等到一组异步操作都结束了,不管每一个操作是成功还是失败,再进行下一步操作在ES2020引入了Promise.allSettled()方法,用来确定一组异步操作是否都结束了(不管成功或失败)
Promise.allSettled()方法接受一个数组作为参数,数组的每个成员都是一个 Promise 对象,并返回一个新的 Promise 对象。只有等到参数数组的所有 Promise 对象都发生状态变更(不管是fulfilled还是rejected),返回的 Promise 对象才会发生状态变更。
- 混合成功与失败的 Promise
无论输入 Promise 是成功(fulfilled)或失败(rejected),Promise.allSettled() 均会等待所有 Promise 完成,并返回一个包含每个结果状态和值的数组const p1 = Promise.resolve("成功1");const p2 = Promise.reject("失败2");const p3 = new Promise(resolve => setTimeout(resolve, 100, "成功3"));Promise.allSettled([p1, p2, p3]).then(results => {results.forEach(result => console.log(result));});/* 输出:{ status: 'fulfilled', value: '成功1' }{ status: 'rejected', reason: '失败2' }{ status: 'fulfilled', value: '成功3' } */
- 错误处理场景:统一处理全部结果
在需要 批量处理异步操作且容忍部分失败 的场景中,Promise.allSettled() 可避免因单个失败中断流程// 模拟多个 API 请求(部分可能失败) const requests = [fetch("/api/user"),fetch("/api/orders").catch(() => "订单请求失败"),fetch("/invalid-url").catch(error => error.message) ];Promise.allSettled(requests).then(results => {results.forEach((result, index) => {if (result.status === "fulfilled") console.log(`请求 ${index + 1} 成功:`, result.value);else console.error(`请求 ${index + 1} 失败:`, result.reason);});});
- 对比 Promise.all():忽略部分失败
当需要 仅关注成功结果,但允许部分失败 时,可通过 filter 过滤已完成的成功值const promises = [Promise.resolve("数据A"),Promise.reject("错误B"),Promise.resolve("数据C") ];Promise.allSettled(promises).then(results => {const successfulData = results.filter(result => result.status === "fulfilled").map(result => result.value);console.log("有效数据:", successfulData); // 输出 ["数据A", "数据C"]});
- 实际应用:批量操作结果统计
适用于需要汇总异步操作结果的场景,如 批量文件上传、多服务状态检查 等// 模拟文件上传(部分成功/失败) const uploadTasks = [{ id: 1, task: Promise.resolve("文件1上传成功") },{ id: 2, task: Promise.reject("文件2上传超时") },{ id: 3, task: Promise.resolve("文件3上传成功") } ];Promise.allSettled(uploadTasks.map(task => task.task)).then(results => {const successCount = results.filter(r => r.status === "fulfilled").length;const errorCount = results.filter(r => r.status === "rejected").length;console.log(`成功:${successCount},失败:${errorCount}`); // 输出 "成功:2,失败:1"});
- 注意事项
结果顺序与输入顺序一致,与完成时间无关68;
未完成的 Promise 仍会阻塞最终结果返回
- Promise.any()
ES2021 引入了Promise.any()方法。该方法接受一组 Promise 实例作为参数,包装成一个新的 Promise 实例返回。Promise.any()跟Promise.race()方法很像,只有一点不同,就是Promise.any()不会因为某个 Promise 变成rejected状态而结束,必须等到所有参数 Promise 变成rejected状态才会结束
- 返回值类型
异步兑现:只要有一个 Promise 成功,返回的 Promise 即以其值兑现13。
异步拒绝:- 所有 Promise 均失败时,返回的 Promise 以 AggregateError 拒绝,其 errors 属性包含所有失败原因。
- 若传入空的可迭代对象,立即拒绝并抛出 AggregateError
- 与 Promise.race() 的区别
Promise.race() 返回第一个解决(无论成功或失败)的 Promise
Promise.any() 仅关注第一个成功的 Promise,忽略所有拒绝,直到全部失败才抛出错误const promises = [Promise.reject('Error 1'),Promise.reject('Error 2'),new Promise((reject) => setTimeout(reject, 100, 'Delayed')),Promise.reject('Error 3'), ];Promise.any(promises).then((value) => console.log(value)) .catch((error) => console.error(error.errors)); // [ 'Error 1', 'Error 2', 'Delayed', 'Error 3' ]
- Promise.resolve()
有时需要将现有对象转为 Promise 对象,Promise.resolve()方法就起到这个作用
Promise.resolve()等价于下面的写法
Promise.resolve()方法的参数分成四种情况。Promise.resolve('foo') // 等价于 new Promise(resolve => resolve('foo'))
- 参数是一个 Promise 实例
如果参数是 Promise 实例,那么Promise.resolve将不做任何修改、原封不动地返回这个实例。const p1 = new Promise(resolve => resolve('success'));const p2 = Promise.resolve(p1);console.log(p1 === p2); // 输出 true
- 参数是一个thenable对象
参数为实现了 then() 方法的对象(thenable),自动转换为 Promise。const thenable = {then(resolve) { resolve(42); } }; Promise.resolve(thenable).then(data => console.log(data));
- 参数不是具有then()方法的对象,或根本就不是对象
如果参数是一个原始值,或者是一个不具有then()方法的对象,则Promise.resolve()方法返回一个新的 Promise 对象,状态为resolved。const p = Promise.resolve('Hello');p.then(function (s) {console.log(s) }); // Hello
- 不带有任何参数
Promise.resolve()方法允许调用时不带参数,直接返回一个resolved状态的 Promise 对象。Promise.resolve().then(() => console.log('微任务执行')); setTimeout(() => console.log('宏任务执行'), 0); // 输出顺序:微任务执行 → 宏任务执行
- 参数是一个 Promise 实例
- Promise.reject()
Promise.reject(reason)方法也会返回一个新的 Promise 实例,该实例的状态为rejected。
Promise.reject()方法的参数,会原封不动地作为reject的理由,变成后续方法的参数。const p = Promise.reject('出错了'); // 等同于 const p = new Promise((resolve, reject) => reject('出错了'))p.then(null, function (s) {console.log(s) }); // 出错了
- Promise.try()
不知道或者不想区分,函数f是同步函数还是异步操作,但是想用 Promise 来处理它。因为这样就可以不管f是否包含异步操作,都用then方法指定下一步流程,用catch方法处理f抛出的错误。
如果f是同步函数,会在本轮事件循环的末尾执行。
- 替代立即执行异步函数 替代 (async () => { … })() 写法。
async () => f()会吃掉f()抛出的错误。所以,如果想捕获错误,要使用promise.catch方法// 传统写法 (async () => {try {const data = await fetchData();} catch (err) { /* ... */ } })();// Promise.try 写法 Promise.try(fetchData).then(data => console.log(data)).catch(err => console.error(err));
- 包装第三方库函数
将可能抛出同步异常的库函数封装为 Promise 接口。// 假设第三方库函数可能同步报错 const parseJSON = str => JSON.parse(str); Promise.try(() => parseJSON('{invalid json}')).catch(err => console.error('JSON解析失败:', err.message)); // 输出错误信息:ml-citation{ref="5" data="citationList"}
- 结合 Promise 链式调用
在复杂链式调用中统一错误处理入口。Promise.try(() => {return getUserSession(); // 可能同步或异步报错 }).then(session => fetchUserData(session.id)).then(data => renderUI(data)).catch(err => showErrorToast(err.message));:ml-citation{ref="5,8" data="citationList"}
- 处理混合型函数
兼容返回值、Promise 或抛异常的函数。function mixedFunc(flag) {if (flag) return 42; // 同步返回值if (flag === null) return Promise.reject('拒绝'); // 异步拒绝throw new Error('异常'); // 同步报错 }Promise.try(() => mixedFunc(null)).catch(err => console.error(err)); // 输出 "拒绝":ml-citation{ref="5" data="citationList"}
- 核心优势总结:
- 错误处理统一化:同步错误自动转为异步拒绝,避免遗漏未捕获异常58。
- 接口规范化:强制函数返回 Promise,消除同步/异步代码差异5。
- 代码简洁性:减少冗余的 try/catch 或 new Promise 封装代码