一尘不染

Promise.all消耗了我所有的RAM

javascript

我为我使用的API提供了一个速率限制器,它每秒允许20个请求。所有请求均基于承诺,并且一旦有响应,承诺将使用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被解析而不需要解析的数据呢?


阅读 321

收藏
2020-05-01

共1个答案

一尘不染

如果您没有58k的诺言,它们关联的异步操作和它们的结果数据一次都处于活动状态,则将使用较少的内存。

相反,您想一次运行X次操作,然后在一次操作完成时,就开始下一个操作,一次不超过X个,一次不超过X个承诺。

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

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

Bluebird’sPromise.map()有一个并发选项可以为您做到这一点,但是也有很多方法可以同时为飞行中的X进行编码。


如果不需要解析的数据,则可以通过如下替换它来使其尽快被GC化:

  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();
    });
}
2020-05-01