使用場景 在開發中,我們可能會遇到一些對非同步請求數做併發量限制的場景,比如說微信小程式的request併發最多為5個,又或者我們需要做一些批量處理的工作,可是我們又不想同時對伺服器發出太多請求(可能會對伺服器造成比較大的壓力)。這個時候我們就可以對請求併發數進行限制,並且使用排隊機制讓請求有序的發送 ...
使用場景
在開發中,我們可能會遇到一些對非同步請求數做併發量限制的場景,比如說微信小程式的request併發最多為5個,又或者我們需要做一些批量處理的工作,可是我們又不想同時對伺服器發出太多請求(可能會對伺服器造成比較大的壓力)。這個時候我們就可以對請求併發數進行限制,並且使用排隊機制讓請求有序的發送出去。
介紹
那麼,接下來我們就來講一下如何實現一個通用的能對請求併發數進行限制的RequestDecorator。我們先來介紹一下它的功能:
- 既然涉及到併發數限制,它就肯定允許用戶傳入最大併發數限制參數:maxLimit
- 既然是一個通用的RequestDecorator,那麼它應該允許使用者傳入其喜歡的非同步api(比如ajax, fetch, axios等)。
- 為了方便起見,也為了開發便利性,被RequestDecorator封裝後的request請求結果都返回一個promise。
- 由於使用者傳入的非同步api不一定是promise類型的,也可能是callback類型的,因此我們提供用戶一個needChange2Promise參數,使用者若傳入的是callback類型的api,它可以通過將這個參數設置為true來將callback類型轉化為promise類型。
分析完功能後,接下來我們就來實現這個東西:
實現
具體代碼如下,每一步我基本都做了註釋,相信大家能看懂。
const pify = require('pify');
class RequestDecorator {
constructor ({
maxLimit = 5,
requestApi,
needChange2Promise,
}) {
// 最大併發量
this.maxLimit = maxLimit;
// 請求隊列,若當前請求併發量已經超過maxLimit,則將該請求加入到請求隊列中
this.requestQueue = [];
// 當前併發量數目
this.currentConcurrent = 0;
// 使用者定義的請求api,若用戶傳入needChange2Promise為true,則將用戶的callback類api使用pify這個庫將其轉化為promise類的。
this.requestApi = needChange2Promise ? pify(requestApi) : requestApi;
}
// 發起請求api
async request(...args) {
// 若當前請求數併發量超過最大併發量限制,則將其阻斷在這裡。
// startBlocking會返回一個promise,並將該promise的resolve函數放在this.requestQueue隊列里。這樣的話,除非這個promise被resolve,否則不會繼續向下執行。
// 當之前發出的請求結果回來/請求失敗的時候,則將當前併發量-1,並且調用this.next函數執行隊列中的請求
// 當調用next函數的時候,會從this.requestQueue隊列里取出隊首的resolve函數並且執行。這樣,對應的請求則可以繼續向下執行。
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('當前併發數:', this.currentConcurrent);
this.currentConcurrent--;
this.next();
}
}
// 新建一個promise,並且將該reolsve函數放入到requestQueue隊列里。
// 當調用next函數的時候,會從隊列里取出一個resolve函數並執行。
startBlocking() {
let _resolve;
let promise2 = new Promise((resolve, reject) => _resolve = resolve);
this.requestQueue.push(_resolve);
return promise2;
}
// 從請求隊列里取出隊首的resolve並執行。
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設置併發量限制,needChange2Promise將callback類型的請求api轉化為promise類型的。
const requestInstance = new RequestDecorator({
maxLimit: 5,
requestApi: delay,
needChange2Promise: true,
});
let promises = [];
for (let i = 0; i < 30; i++) {
// 接下來你就可以像原來使用你的api那樣使用它,參數和原來的是一樣的
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();
這樣,一個能對請求併發數做限制的通用RequestDecorator就已經實現了。當然,這裡還有很多可以繼續增加的功能點,比如
- 允許使用者設置每個請求的retry次數。
- 允許使用者對每個請求設置緩存處理。
優點:
- 不修改用戶原來的request api代碼。對原有代碼無副作用。
- 不修改request api的調用方式。用戶可以無縫的使用被RequestDecorator封裝過的request。
- 可擴展,後續可能不止支持併發量限制,還可能增加緩存、retry等額外的功能。
結語
以上,就是本篇的全部內容。github倉庫地址點擊這裡。歡迎大家點贊或者star下。如果大家有興趣的話,也可以一起來完善這個東西。這個項目還不成熟,可能還會有bug,歡迎大家在github上提issue幫助我完善它。如果覺得有幫助的話,麻煩點個贊哦,謝謝。