不到50行代碼實現一個能對請求併發數作限制的通用RequestDecorator

使用場景

在開發中,咱們可能會遇到一些對異步請求數作併發量限制的場景,好比說微信小程序的request併發最多爲5個,又或者咱們須要作一些批量處理的工做,但是咱們又不想同時對服務器發出太多請求(可能會對服務器形成比較大的壓力)。這個時候咱們就能夠對請求併發數進行限制,而且使用排隊機制讓請求有序的發送出去。javascript

介紹

那麼,接下來咱們就來說一下如何實現一個通用的能對請求併發數進行限制的RequestDecorator。咱們先來介紹一下它的功能:java

  1. 既然涉及到併發數限制,它就確定容許用戶傳入最大併發數限制參數:maxLimit
  2. 既然是一個通用的RequestDecorator,那麼它應該容許使用者傳入其喜歡的異步api(好比ajax, fetch, axios等)。
  3. 爲了方便起見,也爲了開發便利性,被RequestDecorator封裝後的request請求結果都返回一個promise。
  4. 因爲使用者傳入的異步api不必定是promise類型的,也多是callback類型的,所以咱們提供用戶一個needChange2Promise參數,使用者若傳入的是callback類型的api,它能夠經過將這個參數設置爲true來將callback類型轉化爲promise類型。

分析完功能後,接下來咱們就來實現這個東西:ios

實現

具體代碼以下,每一步我基本都作了註釋,相信你們能看懂。git

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;

樣例代碼以下:github

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就已經實現了。固然,這裏還有不少能夠繼續增長的功能點,好比ajax

  1. 容許使用者設置每一個請求的retry次數。
  2. 容許使用者對每一個請求設置緩存處理。

優勢:axios

  1. 不修改用戶原來的request api代碼。對原有代碼無反作用。
  2. 不修改request api的調用方式。用戶能夠無縫的使用被RequestDecorator封裝過的request。
  3. 可擴展,後續可能不止支持併發量限制,還可能增長緩存、retry等額外的功能。

結語

以上,就是本篇的所有內容。github倉庫地址點擊這裏。歡迎你們點贊或者star下。若是你們有興趣的話,也能夠一塊兒來完善這個東西。這個項目還不成熟,可能還會有bug,歡迎你們在github上提issue幫助我完善它。若是以爲有幫助的話,麻煩點個贊哦,謝謝。小程序

本文地址在->本人博客地址, 歡迎給個 start 或 follow微信小程序

相關文章
相關標籤/搜索