不到50行代碼實現(xiàn)一個能對請求并發(fā)數(shù)做限制的通用RequestDecorator

使用場景

在開發(fā)中择份,我們可能會遇到一些對異步請求數(shù)做并發(fā)量限制的場景,比如說微信小程序的request并發(fā)最多為5個赤炒,又或者我們需要做一些批量處理的工作,可是我們又不想同時對服務(wù)器發(fā)出太多請求(可能會對服務(wù)器造成比較大的壓力)亏较。這個時候我們就可以對請求并發(fā)數(shù)進行限制莺褒,并且使用排隊機制讓請求有序的發(fā)送出去。

介紹

那么雪情,接下來我們就來講一下如何實現(xiàn)一個通用的能對請求并發(fā)數(shù)進行限制的RequestDecorator遵岩。我們先來介紹一下它的功能:

  1. 既然涉及到并發(fā)數(shù)限制,它就肯定允許用戶傳入最大并發(fā)數(shù)限制參數(shù):maxLimit

  2. 既然是一個通用的RequestDecorator,那么它應(yīng)該允許使用者傳入其喜歡的異步api(比如ajax, fetch, axios等)巡通。

  3. 為了方便起見尘执,也為了開發(fā)便利性,被RequestDecorator封裝后的request請求結(jié)果都返回一個promise宴凉。

  4. 由于使用者傳入的異步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ù)增加的功能點,比如

  1. 允許使用者設(shè)置每個請求的retry次數(shù)翎嫡。

  2. 允許使用者對每個請求設(shè)置緩存處理欠动。

優(yōu)點:

  1. 不修改用戶原來的request api代碼。對原有代碼無副作用惑申。

  2. 不修改request api的調(diào)用方式具伍。用戶可以無縫的使用被RequestDecorator封裝過的request。

  3. 可擴展圈驼,后續(xù)可能不止支持并發(fā)量限制人芽,還可能增加緩存、retry等額外的功能绩脆。

結(jié)語

以上萤厅,就是本篇的全部內(nèi)容。github倉庫地址點擊這里靴迫。歡迎大家點贊或者star下祈坠。如果大家有興趣的話,也可以一起來完善這個東西矢劲。這個項目還不成熟吹榴,可能還會有bug鸯两,歡迎大家在github上提issue幫助我完善它。如果覺得有幫助的話茫船,麻煩點個贊哦,謝謝拷恨。

本文地址在->本人博客地址, 歡迎給個 start 或 follow

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子剃袍,更是在濱河造成了極大的恐慌,老刑警劉巖捎谨,帶你破解...
    沈念sama閱讀 211,743評論 6 492
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件民效,死亡現(xiàn)場離奇詭異,居然都是意外死亡涛救,警方通過查閱死者的電腦和手機畏邢,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,296評論 3 385
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來检吆,“玉大人舒萎,你說我怎么就攤上這事〔渑妫” “怎么了臂寝?”我有些...
    開封第一講書人閱讀 157,285評論 0 348
  • 文/不壞的土叔 我叫張陵,是天一觀的道長摊灭。 經(jīng)常有香客問我咆贬,道長,這世上最難降的妖魔是什么帚呼? 我笑而不...
    開封第一講書人閱讀 56,485評論 1 283
  • 正文 為了忘掉前任素征,我火速辦了婚禮,結(jié)果婚禮上萝挤,老公的妹妹穿的比我還像新娘御毅。我一直安慰自己,他們只是感情好怜珍,可當我...
    茶點故事閱讀 65,581評論 6 386
  • 文/花漫 我一把揭開白布端蛆。 她就那樣靜靜地躺著,像睡著了一般酥泛。 火紅的嫁衣襯著肌膚如雪今豆。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,821評論 1 290
  • 那天柔袁,我揣著相機與錄音呆躲,去河邊找鬼。 笑死捶索,一個胖子當著我的面吹牛插掂,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 38,960評論 3 408
  • 文/蒼蘭香墨 我猛地睜開眼辅甥,長吁一口氣:“原來是場噩夢啊……” “哼酝润!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起璃弄,我...
    開封第一講書人閱讀 37,719評論 0 266
  • 序言:老撾萬榮一對情侶失蹤要销,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后夏块,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體疏咐,經(jīng)...
    沈念sama閱讀 44,186評論 1 303
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 36,516評論 2 327
  • 正文 我和宋清朗相戀三年脐供,在試婚紗的時候發(fā)現(xiàn)自己被綠了浑塞。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 38,650評論 1 340
  • 序言:一個原本活蹦亂跳的男人離奇死亡患民,死狀恐怖缩举,靈堂內(nèi)的尸體忽然破棺而出垦梆,到底是詐尸還是另有隱情匹颤,我是刑警寧澤,帶...
    沈念sama閱讀 34,329評論 4 330
  • 正文 年R本政府宣布托猩,位于F島的核電站印蓖,受9級特大地震影響,放射性物質(zhì)發(fā)生泄漏京腥。R本人自食惡果不足惜赦肃,卻給世界環(huán)境...
    茶點故事閱讀 39,936評論 3 313
  • 文/蒙蒙 一公浪、第九天 我趴在偏房一處隱蔽的房頂上張望他宛。 院中可真熱鬧欠气,春花似錦、人聲如沸队塘。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,757評論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春涝桅,著一層夾襖步出監(jiān)牢的瞬間烙样,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 31,991評論 1 266
  • 我被黑心中介騙來泰國打工蛤肌, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留批狱,地道東北人赔硫。 一個月前我還...
    沈念sama閱讀 46,370評論 2 360
  • 正文 我出身青樓,卻偏偏與公主長得像权悟,于是被迫代替她去往敵國和親推盛。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 43,527評論 2 349

推薦閱讀更多精彩內(nèi)容

  • 五月底,經(jīng)過的人資考試瘪菌,迎來了論文寫作的忙碌期。 一直在忙诵肛,漸漸地也有些沉不下心疆栏,昨天看世說新語,驚嘆自己的古文閱...
    霽青閱讀 402評論 0 0
  • 洪水泛濫之時珠洗,耶和華坐著為王若专; 耶和華坐著為王,直到永遠膊爪。 (詩篇 29:10) 因為,祂的怒氣不過是轉(zhuǎn)眼之間沛豌; ...
    書亞子君閱讀 1,962評論 0 0
  • 不知道你們有沒有這種感覺加派,經(jīng)常在某一個時間突然就不知道自己到底多大了跳芳,徘徊在上一歲這一歲和下一歲之間,常常覺得自己...
    鶯鶯夏木閱讀 1,167評論 0 1