簡體   English   中英

向一次只能處理20個請求的API發出多個請求

[英]Make several requests to an API that can only handle 20 request a minute

我有一個返回promise的方法,並且在內部該方法調用一個API,該API每分鍾只能有20個請求。 問題是我有很多對象(大約300個),並且我想為每個對象調用API。

目前,我有以下代碼:

    const bigArray = [.....];

    Promise.all(bigArray.map(apiFetch)).then((data) => {
      ...
    });

但是它不處理時序約束。 我希望我可以使用來自lodash _.chunk和_.debounce之類的東西,但是我lodash這個問題。 有人可以幫我嗎?

如果可以使用Bluebird Promise庫,則它具有內置的並發功能,可讓您管理一組異步操作,一次最多可處理N個。

var Promise = require('bluebird');
const bigArray = [....];

Promise.map(bigArray, apiFetch, {concurrency: 20}).then(function(data) {
    // all done here
});

關於這個接口的好處是它將保持20個請求運行。 它將啟動20,然后每次完成一次,它將啟動另一個。 因此,這可能比發送20個,等待所有人完成,再發送20個等等的效率更高。

這也以與bigArray完全相同的順序提供結果,因此您可以確定哪個結果與哪個請求一起進行。

當然,您可以使用計數器使用通用的Promise自己編寫代碼,但是由於它已經在Bluebird庫中構建,我想我建議這樣做。

異步庫也有類似的並發控制,盡管它顯然不是基於promise的。


這是一個僅使用ES6 Promise的手動編碼版本,可保證結果順序並始終保持20個請求在飛行中(直到剩下20個請求為止),以實現最大吞吐量:

function pMap(array, fn, limit) {
    return new Promise(function(resolve, reject) {
        var index = 0, cnt = 0, stop = false, results = new Array(array.length);

        function run() {
            while (!stop && index < array.length && cnt < limit) {
                (function(i) {
                    ++cnt;
                    ++index;
                    fn(array[i]).then(function(data) {
                        results[i] = data;
                        --cnt;
                        // see if we are done or should run more requests
                        if (cnt === 0 && index === array.length) {
                            resolve(results);
                        } else {
                            run();
                        }
                    }, function(err) {
                        // set stop flag so no more requests will be sent
                        stop = true;
                        --cnt;
                        reject(err);
                    });
                })(index);
            }
        }
        run();
    });
}   

pMap(bigArray, apiFetch, 20).then(function(data) {
    // all done here
}, function(err) {
    // error here
});

在這里工作的演示: http : //jsfiddle.net/jfriend00/v98735uu/

您可以每分鍾發送1個包含20個請求的塊,或者每3秒將其間隔1個請求(這可能是API所有者更喜歡的)。

function rateLimitedRequests(array, chunkSize) {
  var delay = 3000 * chunkSize;
  var remaining = array.length;
  var promises = [];
  var addPromises = function(newPromises) {
    Array.prototype.push.apply(promises, newPromises);
    if (remaining -= newPromises.length == 0) {
      Promise.all(promises).then((data) => {
        ... // do your thing
      });
    }
  };
  (function request() {
    addPromises(array.splice(0, chunkSize).map(apiFetch));
    if (array.length) {
      setTimeout(request, delay);
    }
  })();
}

要每3秒撥打1個電話:

rateLimitedRequests(bigArray, 1);

或每分鍾20個:

rateLimitedRequests(bigArray, 20);

如果您更喜歡使用_.chunk_.debounce 1 _.throttle

function rateLimitedRequests(array, chunkSize) {
  var delay = 3000 * chunkSize;
  var remaining = array.length;
  var promises = [];
  var addPromises = function(newPromises) {
    Array.prototype.push.apply(promises, newPromises);
    if (remaining -= newPromises.length == 0) {
      Promise.all(promises).then((data) => {
        ... // do your thing
      });
    }
  };
  var chunks = _.chunk(array, chunkSize);  
  var throttledFn = _.throttle(function() {
    addPromises(chunks.pop().map(apiFetch));
  }, delay, {leading: true});
  for (var i = 0; i < chunks.length; i++) {
    throttledFn();
  }
}

1您可能需要_.throttle因為它在延遲后執行每個函數調用,而_.debounce將多個調用分組為一個調用。 看到這個文章從鏈接的文檔

防抖動 :可以將其視為“將多個事件組合在一起”。 想象您回家,進入電梯,門正在關閉……突然間,您的鄰居出現在大廳里,試圖跳上電梯。 要有禮貌! 並為他打開門:您正在取消電梯的離開。 考慮到第三人可能再次發生相同的情況,依此類推……可能會將出發時間延遲了幾分鍾。

油門 :將其視為閥門,它調節執行流程。 我們可以確定某個函數在特定時間內可以被調用的最大次數。 因此,在電梯類比中,您很有禮貌地允許人們進入10秒鍾,但是一旦延遲過去,您就必須走!

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM