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

Promise基础使用

前言

不知道有没有小伙伴们和我一样,Promise一看就会,一碰到面试题就跪,费尽心思研究懂了,过一段时间就又不会了。今天我们就来花一些时间,让你的Promise彻底毕业。

本文是Promise系列的第一篇:Promise基础使用


文章目录

  • 前言
  • 一、Promise是什么?
  • 二、为什么需要Promise?
  • 三、基本用法
    • 1.创建Promise
    • 2.使用
    • 3.链式调用
    • 4.静态方法
      • 4.1 Promise.all()
      • 4.2 Promise.allSettled()
      • 4.3 Promise.any()
      • 4.4 Promise.race()
      • 4.5 Promise.reject()
      • 4.5 Promise.resolve()
      • 4.6 Promise.try()
      • 4.7 Promise.withResolvers()
  • 四、async/await
    • 4.1 async
    • 4.2 await


一、Promise是什么?

一个 Promise 是一个代理,它代表一个在创建 promise 时不一定已知的值。它允许你将处理程序与异步操作的最终成功值或失败原因关联起来。这使得异步方法可以像同步方法一样返回值:异步方法不会立即返回最终值,而是返回一个 promise,以便在将来的某个时间点提供该值。

简单来说,Promise是一个对象,用来表示一个异步操作的结果。包括三个状态:Pending(进行中)、Fulfilled(成功)、Rejected(失败)。一旦一个Promise状态确定后,状态不会再改变。

二、为什么需要Promise?

在不使用Promise的方案里,要在一个异步任务完成后做某些事情,需要通过回调函数的形式,多个异步任务嵌套执行时,会形成回调地狱。
例如:

// 根据 userId 获取用户信息
// 根据用户信息中的 id 获取订单列表
// 根据第一个订单 orderId 获取订单详情getUser(userId,(userInfo)=>{getOrdersList(userInfo.id,(order)=>{getDetails(order.id,(orderDetail)=>{console.log(orderDetail)})})})

嵌套层级过多时,会使代码变得难以维护。故Promise应运而生。

三、基本用法

1.创建Promise

const promise = new Promise((resolve, reject) => {// 异步操作(如请求API、读取文件等)if (/* 成功 */) {resolve("成功结果"); // 状态变为 Fulfilled} else {reject("错误原因"); // 状态变为 Rejected}
});

把异步操作放在promise中,当执行出结果后调用resolve,即可通过.then()获取到返回结果。
而一些现代的API,比如Fetch已经默认返回为promise,可以直接调用.then()方法。

2.使用

promise.then((result) => {// 处理成功结果}(reject)=>{// 被拒绝时执行}).catch((error) => {// 处理错误 }).finally(() => {// 无论成功失败都会执行(如清理操作)});

通过.then()为Promise兑现或者拒绝设置回调函数,并获取结果。
通过.catch() 注册一个拒绝后调用的函数
通过.finally() 注册一个兑现或者拒绝都会执行的函数

有一个细节问题,既然.then的第二个参数和.catch都可以捕获错误,那么有什么区别呢?

.then(_,errorHandler).catch()
仅捕获当前 Promise 的失败捕获整个链中所有未被处理的错误
处理特定步骤的失败处理全局或兜底的失败

在项目中建议优先使用.catch方法,进行全局性的错误兜底。

3.链式调用

每个 .then()或者.catch() 返回一个新的 Promise,可串联多个操作。让我们来改写上面回调地狱的例子;(这里假设getUser等函数返回的都是promise)

getUser(userId).then((userInfo) => getOrders(userInfo.id)).then((order) => getDetails(order.id)).then((orderDetail) => console.log(orderDetail)).catch((error) => handleError(error));

很多小伙伴到这里可能会疑惑, .then()返回一个新的promise,那么新的promise的状态是什么?
新promise的状态共有以下几种情况:

then返回promise的状态根据回调函数返回值的不同分为以下几种情况:

1.返回一个值:p 以该返回值作为其兑现值。
2.没有返回任何值:p 以 undefined 作为其兑现值。
3.抛出一个错误:p 抛出的错误作为其拒绝值。
4.返回一个已兑现的 Promise 对象:p 以该 Promise 的值作为其兑现值。
5.返回一个已拒绝的 Promise 对象:p 以该 Promise 的值作为其拒绝值。
6.返回另一个待定的 Promise 对象:p 保持待定状态,并在该 Promise 对象被兑现/拒绝后立即以该 Promise 的值作为其兑现/拒绝值。

const promise = new Promise((resolve, reject) => {resolve("err");
});const a = promise.then((result) => {}); // Fulfilled
const a = promise.then((result) => "success"); // Fulfilled
const a = promise.then(() => {throw new Error("err")});   // rejected
// reject
const a = promise.then(() => {return Promise.reject("ok");
});
// Fulfilled
const a = promise.then(() => {return Promise.resolve("ok");
});const a = promise.then(() => {return new Promise((resolve, reject) => {setTimeout(() => {resolve("ok");}, 1000);});
});
// pending
setTimeout(() => {console.log(a);
});
// fulfilled
setTimeout(() => {console.log(a);},2000)

如果then()方法未提供对应回调 ,则透传前一个状态

// 未提供对应回调 → 透传前一个状态
const promise = new Promise((resolve, reject) => {reject("err");
});const a = promise.then();setTimeout(() => {console.log(a);   // Promise {<rejected>: 'err'}
});

4.静态方法

4.1 Promise.all()

接受一个 Promise 可迭代对象作为输入,并返回一个 Promise。当所有输入的 Promise 都被兑现时,返回的 Promise 也将被兑现,兑现值为每一个promise兑现值的数组;任意一个promise失败,则兑现为reject

p1 = new Promise((resolve,reject)=>{reject('err)})
p2 = new Promise((resolve,reject)=>{resolve('success')})
// Promise {<rejected>: 'err'}
Promise.all([p1, p2]).then((values) => {console.log(values);
});

4.2 Promise.allSettled()

静态方法将一个 Promise 可迭代对象作为输入,并返回一个单独的 Promise。当所有输入的 Promise 都已敲定时,返回的 Promise 将被兑现

与Promise.all的区别是无论成功或失败,都会等待所有Promise完成,返回每个的结果状态对象。

p1 = new Promise((resolve,reject)=>{reject('err)})
p2 = new Promise((resolve,reject)=>{resolve('success')})// [{status: 'rejected', reason: 'err'}, {status: 'fulfilled', value: 'success'}]
Promise.allSettled([p1 , p2 ]).then((values) => {console.log(values);
});

4.3 Promise.any()

将一个 Promise 可迭代对象作为输入,并返回一个 Promise。当输入的任何一个 Promise 兑现时,这个返回的 Promise 将会兑现,并返回第一个兑现的值;当所有输入 Promise 都被拒绝,返回拒绝

const promise1 = new Promise((resolve, reject) => {setTimeout(resolve, 500, "one");
});const promise2 = new Promise((resolve, reject) => {reject('err')
});
// one
Promise.any([promise1, promise2]).then((value) => {console.log(value);
})

4.4 Promise.race()

返回第一个兑现的promisede 结果,无论成功还是失败

const promise1 = new Promise((resolve, reject) => {setTimeout(resolve, 500, "one");
});const promise2 = new Promise((resolve, reject) => {reject('err')
});Promise.race([promise1, promise2]).catch((value) => {console.log(value); // err
});

4.5 Promise.reject()

返回一个已拒绝(rejected)的 Promise 对象

	const a = Promise.reject('err')console.log(a) // Promise {<rejected>: 'err'}

4.5 Promise.resolve()

返回一个已解决(resolve)的 Promise 对象,共有以下几种情况:

  // 返回一个值,则返回的 Promise 对象将以该值兑现const a = Promise.resolve('success')console.log(a) // Promise {<fulfilled>: 'success'}// 返回另一个promise,它将返回同一Promise 实例const p1 = Promise.resolve(123)p2 = Promise.resolve(p1)p2.then(res=>console.log(res)) // 123p2 === p1 // true// 返回 thenable 对象(即具有 then 方法的对象)会被转换为 Promise,并立即执行其 then 方法。const thenable = {then: function(resolve) {resolve("Resolved from thenable");}};Promise.resolve(thenable).then((result) => console.log(result)); // 输出 "Resolved from thenable"// 等价于如下:new promise((resolve)=>{resolve("Resolved from thenable")})

4.6 Promise.try()

接受一个任意类型的回调函数(无论其是同步或异步,返回结果或抛出异常),并将其结果封装成一个 Promise
返回值状态如下:
已兑现的,如果 func 同步地返回一个值。
已拒绝的,如果 func 同步地抛出一个错误。
异步兑现或拒绝的,如果 func 返回一个 promise。

	Promise.try(()=>{console.log(1)})  // Promise {<fulfilled>: undefinedPromise.try(()=>{throw 'err'})  // Promise {<rejected>: 'err'}Promise.try(()=>new Promise(resolve=>setTimeout(()=>resolve(1),2000))) // Promise {<pending>}  2s后变为fulfilled

应用场景:
一般用来消除同步与异步错误的处理差异,确保代码健壮性;

// 无论函数同步抛错还是异步拒绝,错误均被.catch捕获
function riskyOperation() {if (Math.random() > 0.5) throw new Error('同步错误');return fetchData(); // 返回Promise
}
Promise.try(riskyOperation).catch(error => console.error('捕获所有错误:', error));

4.7 Promise.withResolvers()

方法返回一个对象,其包含一个新的 Promise 对象和两个函数

等价于

let resolve, reject;
const promise = new Promise((res, rej) => {resolve = res;reject = rej;
});

应用场景:
一般用来耦 Promise 的构造与解决逻辑,可以用来处理一些复杂逻辑,增加代码可读性。
例如我们设计一个可以取消的异步任务

// 原写法
function cancellableDelay(ms) {let resolve, reject; // 手动声明 resolve/reject 的引用let timeoutId;const promise = new Promise((res, rej) => {resolve = res;      // 将 resolve 保存到外部变量reject = rej;       // 将 reject 保存到外部变量timeoutId = setTimeout(() => resolve('完成'), ms);});return {promise,cancel: () => {clearTimeout(timeoutId);reject(new Error('已取消')); // 从外部调用 reject}};
}
// 使用withResolvers
function cancellableDelay(ms) {const { promise, resolve, reject } = Promise.withResolvers();const timeoutId = setTimeout(() => resolve('完成'), ms);return {promise,cancel: () => {clearTimeout(timeoutId);reject(new Error('已取消'));}};
}

四、async/await

Promise的链式调用在层级过多的时候依旧很难以维护,于是出现了一种新的解决方案,async/await。将异步变同步,极大的提高了代码的可读性。

4.1 async

async 关键字用来声明一个异步函数,函数具有以下特性:

1.函数体内允许使用 await 关键字;
2. 每次调用异步函数时,都会返回一个新的 Promise 对象;该对象将会被解决为异步函数的返回值,或者被拒绝为异步函数中未捕获的异常

async function aa(){return 'async'
}	
aa()  // Promise {<fulfilled>: 'async'}async function aa(){throw 'err'
}
aa() // Promise {<rejected>: 'err'}

3.不包含 await 表达式的异步函数是同步运行的,包含 await 表达式,则异步函数就一定会异步完成

async function aa(){console.log(1);
}
aa();
console.log(2)  // 输出:1  2async function aa(){await 1console.log(1);
}
aa();
console.log(2)  // 输出:2  1

4.2 await

await 操作符用于等待一个 Promise 兑现并获取它兑现之后的值。它只能在异步函数或者模块顶层中使用,特性如下:

1.await 表达式通过暂停执行异步函数,直到返回的 promise 被兑现或拒绝。返回的 promise 的解决值会被当作该 await 表达式的返回值

以下代码是等价的:

async function foo() {await 1;
}
function foo() {return Promise.resolve(1).then(() => undefined);
}

2.await 表达式之后的代码可以被认为存在于 .then 回调中

async function foo(name) {console.log(name, "start");await console.log(name, "middle");console.log(name, "end");
}foo("First");
foo("Second");// First start
// First middle
// Second start
// Second middle
// First end
// Second end// 等价于:
function foo(name) {return new Promise((resolve) => {console.log(name, "start");resolve(console.log(name, "middle"));}).then(() => {console.log(name, "end");});
}

到这里,promise的基础使用我们已经分享完毕,下一篇我们分享项目中promise中的应用。

相关文章:

  • 同步 / 异步、阻塞 / 非阻塞
  • Ubuntu手动安装Consul 的详细步骤
  • Linux常用命令33——sudo授权普通用户执行管理员命令
  • go基于redis+jwt进行用户认证和权限控制
  • 【软件设计师:算法】3.排序算法
  • 服务器数据恢复—硬盘坏道导致EqualLogic存储不可用的数据恢复
  • [dify]官方模板DeepResearch工作流学习笔记
  • JGL021垃圾渗滤液小试实验装置
  • cuda程序兼容性问题
  • 虚拟环境直接使用模块 笔记
  • 多边形生成立面点云
  • eFish-SBC-RK3576工控板音频接口测试操作指南
  • AI——认知建模工具:ACT-R
  • Python cv2边缘检测与轮廓查找:从理论到实战
  • 「动态规划」线性DP:股票问题合集 / LeetCode 121|122|123|188 (C++)
  • ubuntu中解决matplotlib无法显示中文问题
  • 使用非常便捷,可以批量操作的小软件
  • windows10系统:如何使用电脑控制手机上多个应用程序(app)?
  • Spring Boot 多数据源事务管理
  • 获取嵌入(Embeddings)的方法与实践
  • 种罂粟喂鸡防病?四川广元一村民非法种植毒品原植物被罚​
  • 人民日报刊文:加快解放和发展新质战斗力
  • 巴基斯坦外长:近期军事回应是自卫措施
  • 领证不用户口本,还需哪些材料?补领证件如何操作?七问七答
  • 巴基斯坦关闭全部领空
  • 习近平同俄罗斯总统普京茶叙