題解Promise併發控制
阿新 • • 發佈:2022-03-15
https://juejin.cn/post/6916317088521027598
https://juejin.cn/post/6916317088521027598
Promise
1. 理解
Promise是非同步程式設計的一種解決方案。
Promise 是一個建構函式,接收一個函式作為引數,返回一個 Promise 例項
Promise的例項有三個狀態:
- Pending(進行中)
- Resolved(已完成)
- Rejected(已拒絕)
Promise的例項有兩個過程: - pending -> fulfilled : Resolved(已完成)
- pending -> rejected:Rejected(已拒絕)
注意:一旦從進行狀態變成為其他狀態就永遠不能更改狀態了。
注意:在構造 Promise 的時候,建構函式內部的程式碼是立即執行的。
2.方法
Promise有五個常用的方法:then()、catch()、all()、race()、finally。
- then()
then方法可以接受兩個回撥函式作為引數。第一個回撥函式是Promise物件的狀態變為resolved時呼叫,第二個回撥函式是Promise物件的狀態變為rejected時呼叫。其中第二個引數可以省略。
then方法返回的是一個新的Promise例項(不是原來那個Promise例項)。因此可以採用鏈式寫法,即then方法後面再呼叫另一個then方法。 - catch()
該方法相當於then方法的第二個引數,指向reject的回撥函式。不過catch方法還有一個作用,就是在執行resolve回撥函式時,如果出現錯誤,丟擲異常,不會停止執行,而是進入catch方法中。 - all()
all方法可以完成並行任務, 它接收一個數組,陣列的每一項都是一個promise物件。當陣列中所有的promise的狀態都達到resolved的時候,all方法的狀態就會變成resolved,如果有一個狀態變成了rejected,那麼all方法的狀態就會變成rejected。 - race()
race方法和all一樣,接受的引數是一個每項都是promise的陣列,但是與all不同的是,當最先執行完的事件執行完之後,就直接返回該promise物件的值。如果第一個promise物件狀態變成resolved,那自身的狀態變成了resolved;反之第一個promise變成rejected,那自身狀態就會變成rejected。 - finally()
finally方法用於指定不管 Promise 物件最後狀態如何,都會執行的操作。該方法是 ES2018 引入標準的。
3. async/await
async是“非同步”的簡寫,await則為等待,所以很好理解async 用於申明一個 function 是非同步的,而 await 用於等待一個非同步方法執行完成。
異常捕獲
async function fn(){
try{
let a = await Promise.reject('error')
}catch(error){
console.log(error)
}
}
async/await對比Promise的優勢
- 程式碼讀起來更加同步,Promise雖然擺脫了回撥地獄,但是then的鏈式調⽤也會帶來額外的閱讀負擔
- Promise傳遞中間值⾮常麻煩,⽽async/await⼏乎是同步的寫法,⾮常優雅
- 錯誤處理友好,async/await可以⽤成熟的try/catch,Promise的錯誤捕獲⾮常冗餘
- 除錯友好,Promise的除錯很差,由於沒有程式碼塊,你不能在⼀個返回表示式的箭頭函式中設定斷點,如果你在⼀個.then程式碼塊中使⽤偵錯程式的步進(step-over)功能,偵錯程式並不會進⼊後續的.then程式碼塊,因為偵錯程式只能跟蹤同步程式碼的每⼀步。
4. 題目
實現一個批量請求函式 multiRequest(urls, maxNum),要求如下:
• 要求最大併發數 maxNum
• 每當有一個請求返回,就留下一個空位,可以增加新的請求
• 所有請求完成後,結果按照 urls 裡面的順序依次打出
5. 解答
// 整體採用遞迴呼叫來實現:最初發送的請求數量上限為允許的最大值,並且這些請求中的每一個都應該在完成時繼續遞迴傳送,通過傳入的索引來確定了urls裡面具體是那個URL,保證最後輸出的順序不會亂,而是依次輸出。
function multiRequest(urls = [], maxNum) {
// 請求總數量
const len = urls.length;
// 根據請求數量建立一個數組來儲存請求的結果
const result = new Array(len).fill(false);
// 當前完成的數量
let count = 0;
return new Promise((resolve, reject) => {
// 請求maxNum個
while (count < maxNum) {
next();
}
function next() {
let current = count++;
// 處理邊界條件
if (current >= len) {
// 請求全部完成就將promise置為成功狀態, 然後將result作為promise值返回
!result.includes(false) && resolve(result);
return;
}
const url = urls[current];
console.log(`開始 ${current}`, new Date().toLocaleString());
fetch(url)
.then((res) => {
// 儲存請求結果
result[current] = res;
console.log(`完成 ${current}`, new Date().toLocaleString());
// 請求沒有全部完成, 就遞迴
if (current < len) {
next();
}
})
.catch((err) => {
console.log(`結束 ${current}`, new Date().toLocaleString());
result[current] = err;
// 請求沒有全部完成, 就遞迴
if (current < len) {
next();
}
});
}
});
}