Promise.all 消耗了我所有的 RAM

IT技术 javascript node.js bluebird
2021-01-31 00:58:27

我有一个我正在使用的 API 的速率限制器,它允许每秒 20 个请求。所有请求都是基于Promise的,一旦有响应,Promise将通过 API 数据解决。

问题:

我设置了一个 promiseArray,其中包含 58k 个所有等待响应的Promise。如此缓慢地内存增加,直到我的内存不足。在我的特定情况下,我不需要将解析的数据传递给我then(),并且数据占用了我所有的 RAM。

代码:

  }).then(() => {
    // 2. Crawl for all clanprofiles from these leaderboards
    const promiseArray = []
    for (let i = 0; i < clanTags.length; i++) {
      // Resolved data from getClanProfile() is eating up all my RAM
      const p = backgroundScheduler.getClanProfile(clanTags[i], true)
      promiseArray.push(p)
    }
    return Promise.all(promiseArray)
  }).then(() => {

那么有没有一种方法可以在不需要解析数据的情况下等待 promiseArray 被解析?

1个回答

如果您没有 58k Promise、它们相关的异步操作和它们的结果数据同时处于活动状态,那么您将使用较少的内存。

相反,您希望一次运行 X 个操作,然后当一个操作完成时,您开始下一个操作,同时在运行中永远不会超过 X,并且一次使用的Promise永远不会超过 X。

您可以尝试使用适当的 X 值。值为 1 是顺序操作,但您通常可以通过使用更高的 X 值来改善整体端到端操作时间。如果所有请求都访问同一主机,则 X 是可能不超过 5-10 个(因为给定的主机不能真正一次做很多事情,并且要求它做的比它一次做的更多只会减慢它的速度)。

如果每个请求都针对不同的主机,那么您可以将 X 设置得更高。实验将为您提供峰值内存使用和总体吞吐量的最佳值,并且在某种程度上取决于您的具体情况。

BluebirdPromise.map()有一个并发选项可以为您执行此操作,但也有多种方法可以同时为 X 进行编码。

以下是管理一次飞行数量的其他一些编码示例:

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

如何串行执行promise?

由于内存不足而无法完成Promise

一次触发 1,000,000 个请求 100 个

如何做到这一点,以便我可以在 javascript 中一次执行 10 个Promise,以防止对 api 调用进行速率限制?


如果你不需要解析的数据,你可以通过像这样替换它来让它更快地被 GCed:

  const p = backgroundScheduler.getClanProfile(clanTags[i], true).then(data => {
      return 0;     // make resolved value just be a simple number
                    // so other data is now eligible for GC
  });
  promiseArray.push(p)    

而且,这是一个简单的实现,它迭代一个数组,同时不超过 X 个请求:

// takes an array of items and a function that returns a promise
// runs no more than maxConcurrent requests at once
function mapConcurrent(items, maxConcurrent, fn) {
    let index = 0;
    let inFlightCntr = 0;
    let doneCntr = 0;
    let results = new Array(items.length);
    let stop = false;
    
    return new Promise(function(resolve, reject) {
        
        function runNext() {
            let i = index;
            ++inFlightCntr;
            fn(items[index], index++).then(function(val) {
                ++doneCntr;
                --inFlightCntr;
                results[i] = val;
                run();
            }, function(err) {
                // set flag so we don't launch any more requests
                stop = true;
                reject(err);
            });
        }
        
        function run() {
            // launch as many as we're allowed to
            while (!stop && inFlightCntr < maxConcurrent && index < items.length) {
                runNext();
            }
            // if all are done, then resolve parent promise with results
            if (doneCntr === items.length) {
                resolve(results);
            }
        }
        
        run();
    });
}
添加了一个实现,用于控制同时“进行中”的请求数。
2021-03-23 00:58:27