向一个每分钟只能处理 20 个请求的 API 发出多个请求

Ign*_*vas 1 javascript node.js

我有一个返回承诺的方法,该方法在内部调用一个 API,该 API 每分钟只能有 20 个请求。问题是我有一大堆对象(大约 300 个),我想为每个对象调用 API。

目前我有以下代码:

    const bigArray = [.....];

    Promise.all(bigArray.map(apiFetch)).then((data) => {
      ...
    });
Run Code Online (Sandbox Code Playgroud)

但它不处理时序约束。我希望我可以使用 _.chunk 和 _.debounce from 之类的东西,lodash但我无法理解它。有人可以帮我吗?

jfr*_*d00 5

如果您可以使用 Bluebird 承诺库,它具有内置的并发功能,可让您管理一组异步操作,一次最多 N 个。

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

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

这个接口的好处是它可以保持 20 个请求。它将启动 20,然后每完成一个,它就会启动另一个。因此,这可能比发送 20 个更有效,等待所有完成,再发送 20 个等等......

这也以完全相同的顺序提供结果,bigArray以便您可以确定哪个结果与哪个请求相符。

当然,您可以使用计数器通过通用承诺自己编写代码,但由于它已经内置在 Bluebird 库中,我想我会推荐这种方式。

Async 库也有类似的并发控制,尽管它显然不是基于承诺的。


这是一个仅使用 ES6 承诺的手工编码版本,它维护结果顺序并始终保持 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
});
Run Code Online (Sandbox Code Playgroud)

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

  • @IgnacioARivas - 添加了手动编码版本,可维护结果顺序并始终保持 20 个请求处于运行状态,并且不使用外部库。 (2认同)