使用場景
在開發(fā)中择份,我們可能會遇到一些對異步請求數(shù)做并發(fā)量限制的場景,比如說微信小程序的request并發(fā)最多為5個赤炒,又或者我們需要做一些批量處理的工作,可是我們又不想同時對服務(wù)器發(fā)出太多請求(可能會對服務(wù)器造成比較大的壓力)亏较。這個時候我們就可以對請求并發(fā)數(shù)進行限制莺褒,并且使用排隊機制讓請求有序的發(fā)送出去。
介紹
那么雪情,接下來我們就來講一下如何實現(xiàn)一個通用的能對請求并發(fā)數(shù)進行限制的RequestDecorator遵岩。我們先來介紹一下它的功能:
既然涉及到并發(fā)數(shù)限制,它就肯定允許用戶傳入最大并發(fā)數(shù)限制參數(shù):maxLimit
既然是一個通用的RequestDecorator,那么它應(yīng)該允許使用者傳入其喜歡的異步api(比如ajax, fetch, axios等)巡通。
為了方便起見尘执,也為了開發(fā)便利性,被RequestDecorator封裝后的request請求結(jié)果都返回一個promise宴凉。
由于使用者傳入的異步api不一定是promise類型的誊锭,也可能是callback類型的,因此我們提供用戶一個needChange2Promise參數(shù)弥锄,使用者若傳入的是callback類型的api丧靡,它可以通過將這個參數(shù)設(shè)置為true來將callback類型轉(zhuǎn)化為promise類型。
分析完功能后籽暇,接下來我們就來實現(xiàn)這個東西:
實現(xiàn)
具體代碼如下温治,每一步我基本都做了注釋,相信大家能看懂戒悠。
const pify = require('pify');
class RequestDecorator {
constructor ({
maxLimit = 5,
requestApi,
needChange2Promise,
}) {
// 最大并發(fā)量
this.maxLimit = maxLimit;
// 請求隊列,若當前請求并發(fā)量已經(jīng)超過maxLimit,則將該請求加入到請求隊列中
this.requestQueue = [];
// 當前并發(fā)量數(shù)目
this.currentConcurrent = 0;
// 使用者定義的請求api熬荆,若用戶傳入needChange2Promise為true,則將用戶的callback類api使用pify這個庫將其轉(zhuǎn)化為promise類的。
this.requestApi = needChange2Promise ? pify(requestApi) : requestApi;
}
// 發(fā)起請求api
async request(...args) {
// 若當前請求數(shù)并發(fā)量超過最大并發(fā)量限制绸狐,則將其阻斷在這里卤恳。
// startBlocking會返回一個promise累盗,并將該promise的resolve函數(shù)放在this.requestQueue隊列里。這樣的話突琳,除非這個promise被resolve,否則不會繼續(xù)向下執(zhí)行若债。
// 當之前發(fā)出的請求結(jié)果回來/請求失敗的時候,則將當前并發(fā)量-1,并且調(diào)用this.next函數(shù)執(zhí)行隊列中的請求
// 當調(diào)用next函數(shù)的時候本今,會從this.requestQueue隊列里取出隊首的resolve函數(shù)并且執(zhí)行。這樣主巍,對應(yīng)的請求則可以繼續(xù)向下執(zhí)行冠息。
if (this.currentConcurrent >= this.maxLimit) {
await this.startBlocking();
}
try {
this.currentConcurrent++;
const result = await this.requestApi(...args);
return Promise.resolve(result);
} catch (err) {
return Promise.reject(err);
} finally {
console.log('當前并發(fā)數(shù):', this.currentConcurrent);
this.currentConcurrent--;
this.next();
}
}
// 新建一個promise,并且將該reolsve函數(shù)放入到requestQueue隊列里。
// 當調(diào)用next函數(shù)的時候孕索,會從隊列里取出一個resolve函數(shù)并執(zhí)行逛艰。
startBlocking() {
let _resolve;
let promise2 = new Promise((resolve, reject) => _resolve = resolve);
this.requestQueue.push(_resolve);
return promise2;
}
// 從請求隊列里取出隊首的resolve并執(zhí)行。
next() {
if (this.requestQueue.length <= 0) return;
const _resolve = this.requestQueue.shift();
_resolve();
}
}
module.exports = RequestDecorator;
樣例代碼如下:
const RequestDecorator = require('../src/index.js')
// 一個callback類型的請求api
function delay(num, time, cb) {
setTimeout(() => {
cb(null, num);
}, time);
}
// 通過maxLimit設(shè)置并發(fā)量限制搞旭,needChange2Promise將callback類型的請求api轉(zhuǎn)化為promise類型的散怖。
const requestInstance = new RequestDecorator({
maxLimit: 5,
requestApi: delay,
needChange2Promise: true,
});
let promises = [];
for (let i = 0; i < 30; i++) {
// 接下來你就可以像原來使用你的api那樣使用它,參數(shù)和原來的是一樣的
promises.push(requestInstance.request(i, Math.random() * 3000).then(result => console.log('result', result), error => console.log(error)));
}
async function test() {
await Promise.all(promises);
}
test();
這樣,一個能對請求并發(fā)數(shù)做限制的通用RequestDecorator就已經(jīng)實現(xiàn)了肄渗。當然镇眷,這里還有很多可以繼續(xù)增加的功能點,比如
允許使用者設(shè)置每個請求的retry次數(shù)翎嫡。
允許使用者對每個請求設(shè)置緩存處理欠动。
優(yōu)點:
不修改用戶原來的request api代碼。對原有代碼無副作用惑申。
不修改request api的調(diào)用方式具伍。用戶可以無縫的使用被RequestDecorator封裝過的request。
可擴展圈驼,后續(xù)可能不止支持并發(fā)量限制人芽,還可能增加緩存、retry等額外的功能绩脆。
結(jié)語
以上萤厅,就是本篇的全部內(nèi)容。github倉庫地址點擊這里靴迫。歡迎大家點贊或者star下祈坠。如果大家有興趣的話,也可以一起來完善這個東西矢劲。這個項目還不成熟吹榴,可能還會有bug鸯两,歡迎大家在github上提issue幫助我完善它。如果覺得有幫助的話茫船,麻煩點個贊哦,謝謝拷恨。
本文地址在->本人博客地址, 歡迎給個 start 或 follow