promise高階用法: 同步執行,併發控制,併發控制並獲取全部執行結果
阿新 • • 發佈:2022-03-22
原文連結:https://www.cnblogs.com/yalong/p/16038528.html
本文展示promise的三種用法
- promise實現非同步程式碼的同步執行
- promise控制大量請求的併發執行
- promise控制大量請求的併發執行,並獲取全部執行結果
一.Promise實現非同步程式碼的同步執行
場景描述:
有fn1 、fn2 、 fn3三個非同步任務,要求必須先執行fn1,再執行fn2,最後執行fn3
且下一次任務必須要拿到上一次任務執行的結果,才能做操作
三個非同步函式如下:
// 非同步函式fn1 const fn1 = function () { return new Promise((resolve, reject) => { setTimeout(() => { resolve('111') }, 500) }) } // 非同步函式fn2 const fn2 = (data) => { return new Promise((resolve, reject) => { resolve(data + '222') }) } // 非同步函式fn3 const fn3 = (data) => { return new Promise((resolve, reject) => { setTimeout(function () { resolve(data + '333') }, 1000) }) }
方法一:使用then的鏈式呼叫
//鏈式呼叫
fn1().then((data) => {
return fn2(data)
}).then((data) => {
return fn3(data)
})
.then((data) => {
console.log(data) // 111222333
})
方法二:使用async await
async function queue(arr) { let res = null for (let promise of arr) { res = await promise(res) } return await res // 這裡的await可以去掉,因為已經是最後一步了 } // 因為async返回返回的也是promise,所以可以使用then queue([fn1, fn2, fn3]) .then(data => { console.log(data) // 111222333 })
二. Promise併發控制
場景描述一
假設有100個ajax請求,每次傳送3個請求,其中一個請求完畢,再加入新的請求,直到全部請求完畢
如果使用promsie.all,瀏覽器會瞬間傳送100個請求,這樣可能會造成請求阻塞、頁面卡頓、甚至伺服器崩潰,顯然不合理;
那麼就需要設計一個方案
程式碼實現
// 這個就是每次執行的非同步請求方法,引數不一樣 const fn = (t) => { // 用setTimeout模擬非同步請求 return new Promise((resolve, reject) => { setTimeout(() => { console.log('任務完成', t, new Date()); resolve({ t, date: new Date() }); }, t * 1000); }) }; let arr = [1, 1, 1, 2, 2, 2, 3, 3, 3] /** * arrs 請求資料來源陣列 * limit 是每次並行發起多少個請求 * handleFn 就是非同步處理函式 */ function limitQueueFn(arrs, limit, handleFn) { // 完成任務數 let index = 0; // 第一次的時候 一次性執行 limit 個任務 for (let i = 0; i < limit; i++) { run(); } // 執行一個任務 function run() { // 構造待執行任務 當該任務完成後 如果還有待完成的任務 繼續執行任務 new Promise((resolve, reject) => { const value = arrs[index]; index++; // 這個是同步操作 // resolve 返回 promise resolve(handleFn(value)) }).then(() => { if (index < arrs.length) { run() } }) } }; limitQueueFn(arr, 3, fn)
執行結果列印如下:
任務完成 1 2022-03-22T03:19:17.907Z
任務完成 1 2022-03-22T03:19:17.911Z
任務完成 1 2022-03-22T03:19:17.911Z
任務完成 2 2022-03-22T03:19:19.913Z
任務完成 2 2022-03-22T03:19:19.914Z
任務完成 2 2022-03-22T03:19:19.915Z
任務完成 3 2022-03-22T03:19:22.919Z
任務完成 3 2022-03-22T03:19:22.919Z
任務完成 3 2022-03-22T03:19:22.919Z
程式碼分析
- 首先並行發起limit個請求
- 然後就是利用promise.then, 當請求完成之後,就去發起下一個請求
- 使用變數 index 來統計執行了多少個請求,沒有執行就就一直執行
場景描述二
上面的程式碼是可以實現併發請求控制了, 但是如果想獲取到全部請求執行完的結果,並且結果也要是有序的,跟 arrs 的順序一樣,這個怎麼實現?
程式碼實現
const fn = (t) => {
// 用setTimeout模擬非同步請求
return new Promise((resolve, react) => {
setTimeout(() => {
console.log('任務完成', t, new Date());
resolve({ t, date: new Date() });
}, t * 1000);
})
};
let arr = [1, 1, 1, 2, 2, 2, 3, 3, 3]
/**
* arrs 請求資料來源陣列
* limit 是每次並行發起多少個請求
* handleFn 就是非同步處理函式
*/
function limitQueueFn(arrs, limit, handleFn) {
// 完成任務數
let runningIndex = 0; // 這是正在執行的下標
let finishedIndex = 0 // 這是已經執行完的下表
let result = new Array(arrs.length).fill(0) // 建立一個空陣列, 儲存結果
return new Promise((resolveFn, rejectFn) => {
// 第一次的時候 一次性執行 limit 個任務
for (let i = 0; i < limit; i++) {
run();
}
// 執行一個任務
function run() {
// 構造待執行任務 當該任務完成後 如果還有待完成的任務 繼續執行任務
new Promise((resolve, reject) => {
const value = arrs[runningIndex];
runningIndex++; // 這個是同步操作
resolve(handleFn(value))
}).then((res) => {
result[finishedIndex] = res
finishedIndex++
if (runningIndex < arrs.length) {
run()
} else { // 全部執行完畢
resolveFn(result)
}
})
}
})
};
limitQueueFn(arr, 3, fn).then(res => {
console.log('結果如下:')
console.log(res)
})
執行結果列印如下:
任務完成 1 2022-03-22T03:18:10.420Z
任務完成 1 2022-03-22T03:18:10.426Z
任務完成 1 2022-03-22T03:18:10.426Z
任務完成 2 2022-03-22T03:18:12.428Z
任務完成 2 2022-03-22T03:18:12.430Z
任務完成 2 2022-03-22T03:18:12.430Z
任務完成 3 2022-03-22T03:18:15.435Z
任務完成 3 2022-03-22T03:18:15.436Z
任務完成 3 2022-03-22T03:18:15.436Z
結果如下:
[ { t: 1, date: 2022-03-22T03:18:10.426Z },
{ t: 1, date: 2022-03-22T03:18:10.426Z },
{ t: 1, date: 2022-03-22T03:18:10.426Z },
{ t: 2, date: 2022-03-22T03:18:12.429Z },
{ t: 2, date: 2022-03-22T03:18:12.430Z },
{ t: 2, date: 2022-03-22T03:18:12.430Z },
{ t: 3, date: 2022-03-22T03:18:15.436Z },
{ t: 3, date: 2022-03-22T03:18:15.436Z },
{ t: 3, date: 2022-03-22T03:18:15.436Z } ]