你必須要知道的,五個(gè)Promise高級(jí)使用技巧
無論是在開發(fā)中還是在面試中 promise 都是一個(gè)非常重要的內(nèi)容。常見的面試中都會(huì)問及到 promise.then()、Promise.all 或者配合 async/await 使用的方式。
但是,一旦我們遇到更高級(jí)別的崗位(比如大廠高P崗位),那么以上這些知識(shí)可能就不夠使用了。所以說,咱們今天就來分享 5個(gè)promise的“高級(jí)”使用技巧,提高對(duì) promise 的理解,幫大家通過高P面試!
技巧 1:Promise 順序執(zhí)行
當(dāng)面對(duì)必須順序執(zhí)行一系列任務(wù)的場(chǎng)景時(shí),我們第一時(shí)間都會(huì)想到 “await”。但是除了 await 之外,使用 Promise 的替代方法可能更加優(yōu)雅。
const requestAry = [() => api.request1(), () => api.request2(), () => api.request3()];
// 使用 `await`
for (const requestItem of requestAry) {
await requestItem();
}
// 使用promise進(jìn)行串行執(zhí)行
const finallyPromise = requestAry.reduce(
(currentPromise, nextRequest) => currentPromise.then(() => nextRequest()),
Promise.resolve()
);
該方法使用“then”函數(shù),可以簡(jiǎn)潔高效地連接 Promise,確保任務(wù)的串行執(zhí)行。
技巧 2: Async/Await 的替代用法
使用 async/await 作為接收異步函數(shù)返回值的這種方式大家應(yīng)該都比較熟悉,但很少有人認(rèn)識(shí)到異步函數(shù)從根本上來說是返回一個(gè) promise。
我們來看下面這個(gè)場(chǎng)景
const fn1 = async () => 1;
const fn2 = () => Promise.resolve(1);
fn1(); // 返回值為1的promise對(duì)象
通常,await 與 Promise 對(duì)象一起使用,等待 promise 被解析。 因此,等待 fn1 函數(shù)也相當(dāng)于以下內(nèi)容:
await fn1();
-----
const promiseInst = fn1();
await promiseInst;
但是,對(duì)于 await 大家需要知道:當(dāng)后面的值不是 Promise 對(duì)象時(shí),它將會(huì)把該值包裝在 Promise 對(duì)象中。 因此,await之后的代碼必須異步執(zhí)行:
Promise.resolve().then(() => {
console.log(1);
});
await 2;
console.log(2);
// 輸出: 1 2
這相當(dāng)于:
Promise.resolve().then(() => {
console.log(1);
});
Promise.resolve().then(() => {
console.log(2);
});
技巧 3:promise 處理連續(xù)的請(qǐng)求
當(dāng)先前的請(qǐng)求仍處于 待處理狀態(tài) 時(shí)發(fā)送重復(fù)的請(qǐng)求可能會(huì)導(dǎo)致不必要的資源消耗。
request('GET', '/test-api').then(response1 => {
// …
});
request('GET', '/test-api').then(response2 => {
// …
});
并且這種場(chǎng)景非常常見:
- 在頁面上渲染多個(gè)內(nèi)部組件同時(shí)獲取數(shù)據(jù)時(shí)。
- 處理提交按鈕未禁用,用戶連續(xù)點(diǎn)擊多次的場(chǎng)景。
- 預(yù)加載數(shù)據(jù)時(shí),在預(yù)加載完成前導(dǎo)航至預(yù)加載頁面。
所以,我們可以對(duì) promise 進(jìn)行對(duì)應(yīng)的封裝處理,利用 緩存策略 解決這個(gè)問題,可以參考以下代碼:
// 緩存對(duì)象
const pendingPromises = {};
function request(type, url, data) {
const requestKey = JSON.stringify([type, url, data]);
// 讀取緩存
if (pendingPromises[requestKey]) {
return pendingPromises[requestKey];
}
const fetchPromise = fetch(url, {
method: type,
data: JSON.stringify(data)
})
.then(response => response.json())
.finally(() => {
delete pendingPromises[requestKey];
});
// 存入緩存
return pendingPromises[requestKey] = fetchPromise;
}
技巧 4:解碼 then/catch/finally 返回值
我們知道 promise 擁有三種狀態(tài)的返回值:
.then 處理
// 返回新的 Promise(resolve => resolve(1))
Promise.resolve().then(() => 1);
// 返回新的 Promise(resolve => resolve(Promise.resolve(2)))
Promise.resolve().then(() => Promise.resolve(2));
// 返回新的 Promise(resolve => resolve(Promise.reject(new Error('abc'))))
Promise.resolve().then(() => { throw new Error('abc') });
// 返回新的 Promise(resolve => resolve(2))
Promise.reject().then(() => 1, () => 2);
.catch 處理
// 返回新的 Promise(resolve => resolve(3))
Promise.reject().catch(() => 3);
// 返回新的 Promise(resolve => resolve(promise object calling catch))
Promise.resolve().catch(() => 4);
.finally 處理
- 當(dāng)finally函數(shù)返回非promise值時(shí),它會(huì)在finally函數(shù)之前返回promise對(duì)象。
// 返回 Promise.resolve()
Promise.resolve().finally(() => {});
// 返回 Promise.reject()
Promise.reject().finally(() => {});
- 當(dāng)finally函數(shù)的返回值是一個(gè)promise時(shí),它會(huì)在finally函數(shù)之前等待promise解析,然后再返回promise對(duì)象。
// 返回處于 pending 狀態(tài)下的 Promise, 1秒后 resolve 為 5。
Promise.resolve(5).finally(() => new Promise(res => {
setTimeout(res, 1000);
}));
Promise.reject(6).finally(() => new Promise(res => {
setTimeout(res, 1000);
})); // 返回處于 pending 狀態(tài)下的 Promise, 在1秒后 reject 為 6。
技巧 5:區(qū)分“then”的第二個(gè)回調(diào)和“catch”回調(diào)
promise 有兩種處理錯(cuò)誤的方式,分別是:
- .then 的第二個(gè)參數(shù)回調(diào)函數(shù)
- .catch 回調(diào)
當(dāng)請(qǐng)求出現(xiàn)錯(cuò)誤時(shí),第二個(gè)回調(diào)函數(shù)和 Promise 的 .catch 都會(huì)被觸發(fā)。
乍一看,它們好像沒什么區(qū)別,但是——前者(第二個(gè)參數(shù)回調(diào)函數(shù))無法捕獲“then”當(dāng)前第一個(gè)回調(diào)函數(shù)中拋出的錯(cuò)誤,而“catch”可以:
Promise.resolve().then(
() => {
throw new Error('成功的回調(diào)出現(xiàn)錯(cuò)誤');
},
() => {
// 這里不會(huì)執(zhí)行
}
).catch(reason => {
console.log(reason.message); // 成功的回調(diào)出現(xiàn)錯(cuò)誤
});