有了這五個(gè)方法,輕松處理異步任務(wù)
在 JavaScript 中,許多操作都是異步的,比如發(fā)起網(wǎng)絡(luò)請(qǐng)求、讀取文件、定時(shí)器等。Promise 提供了一種更加結(jié)構(gòu)化和易于理解的方式來(lái)處理異步操作,使得異步代碼更加清晰易讀,避免了回調(diào)地獄的問(wèn)題。
本文我將介紹 Promise 對(duì)象上 5 個(gè)非常有用的方法,掌握這些方法之后,可以讓你更好地解決工作遇到的一些異步問(wèn)題。
Promise.all()
當(dāng)你需要并行執(zhí)行多個(gè)異步操作,并且只有當(dāng)所有異步操作都成功完成時(shí)才繼續(xù)執(zhí)行后續(xù)代碼。
const promise1 = Promise.resolve("Promise");
const promise2 = new Promise((resolve, reject) => {
setTimeout(resolve, 2000, "is");
});
const promise3 = new Promise((resolve, reject) => {
setTimeout(resolve, 1000, "useful");
});
Promise.all([promise1, promise2, promise3]).then((values) => {
console.log(values);
});
以上代碼成功運(yùn)行后,控制臺(tái)會(huì)輸出以下結(jié)果:
[ 'Promise', 'is', 'useful' ]
但如果某個(gè) promise 對(duì)象在執(zhí)行過(guò)程中拋出異常,比如,promise2 對(duì)象在執(zhí)行時(shí)拋出異常:
const promise2 = new Promise((resolve, reject) => {
setTimeout(reject, 2000, new Error("Crash..."));
});
那么,你就無(wú)法正常獲取 promise1 和 promise3 對(duì)象返回的結(jié)果。針對(duì)這個(gè)問(wèn)題,你可以使用 Promise.allSettled() 方法。
Promise.allSettled()
當(dāng)你想要等待多個(gè)異步操作完成,并且你需要知道每個(gè)異步操作的結(jié)果。
const promise1 = Promise.resolve("Promise");
const promise2 = new Promise((resolve, reject) => {
setTimeout(reject, 2000, new Error("Crash..."));
});
const promise3 = new Promise((resolve, reject) => {
setTimeout(resolve, 1000, "useful");
});
Promise.allSettled([promise1, promise2, promise3]).then((values) => {
console.log(values);
});
運(yùn)行以上代碼之后,控制臺(tái)會(huì)輸出以下結(jié)果。數(shù)組中每個(gè)對(duì)象上的 status 屬性是用于標(biāo)識(shí)對(duì)應(yīng) promise 對(duì)象的執(zhí)行狀態(tài)。
[
{ status: 'fulfilled', value: 'Promise' },
{
status: 'rejected',
reason: Error: Crash...
},
{ status: 'fulfilled', value: 'useful' }
]
Promise.race()
當(dāng)你有多個(gè)異步操作,并且你只關(guān)心哪個(gè)操作最先完成,不管它是成功還是失敗。
const promise1 = new Promise((resolve, reject) => {
setTimeout(reject, 1000, "Promise 1 resolved");
});
const promise2 = new Promise((resolve, reject) => {
setTimeout(resolve, 2000, "Promise 2 resolved");
});
Promise.race([promise1, promise2]).then((value) => {
console.log(value);
});
以上代碼成功運(yùn)行后,控制臺(tái)會(huì)輸出 "Promise 1 resolved"。
Promise.any()
當(dāng)你有多個(gè)異步操作,并且你想要得到第一個(gè)成功的異步操作的結(jié)果,而忽略其他已失敗的異步操作。
const promise1 = Promise.reject("any");
const promise2 = new Promise((resolve) =>
setTimeout(resolve, 100, "Promise 2 resolved")
);
const promise3 = new Promise((resolve) =>
setTimeout(resolve, 500, "Promise 3 resolved")
);
Promise.any([promise1, promise2, promise3]).then((value) => {
console.log(value);
});
以上代碼成功運(yùn)行后,控制臺(tái)會(huì)輸出 "Promise 2 resolved"。
Promise.withResolvers()
在某些場(chǎng)景下,我們希望在外部控制 Promise 對(duì)象的狀態(tài)。比如,在請(qǐng)求網(wǎng)絡(luò)數(shù)據(jù)的場(chǎng)景,當(dāng)成功接收所有數(shù)據(jù)后,才調(diào)用 resolve 方法設(shè)置當(dāng)前 Promise 的返回?cái)?shù)據(jù)。
let resolve, reject;
const promise = new Promise((res, rej) => {
resolve = res;
reject = rej;
});
asyncRequest(config, response => {
const buffer = [];
response.on('callback-request', id => {
promise.then(data => callback(id, data));
});
response.on('data', data => buffer.push(data));
response.on('end', () => resolve(buffer));
response.on('error', reason => reject(reason));
});
上述的場(chǎng)景在日常工作中很常見(jiàn),為了避免重復(fù)寫(xiě)以下代碼。
let resolve, reject;
const promise = new Promise((res, rej) => {
resolve = res;
reject = rej;
});
ts39 增加了 proposal-promise-with-resolvers 草案,目前該草案的狀態(tài)已經(jīng)是 Stage 4。不過(guò)在使用的過(guò)程中,需要注意它的兼容性:
對(duì)于不支持 Promise.withResolvers() 方法的環(huán)境,我們可以通過(guò)通過(guò)自定義函數(shù)來(lái)實(shí)現(xiàn)對(duì)應(yīng)的功能。比如,TypeScript 源碼中,定義了一個(gè) defer 函數(shù)來(lái)實(shí)現(xiàn) Promise.withResolvers() 方法同樣的功能。
export function defer<T = void>(): Deferred<T> {
let resolve!: (value: T | PromiseLike<T>) => void;
let reject!: (reason: unknown) => void;
const promise = new Promise<T>((_resolve, _reject) => {
resolve = _resolve;
reject = _reject;
});
return { resolve, reject, promise };
}