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中的应用。