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