【问题标题】:Promise.all consumes all my RAMPromise.all 消耗了我所有的内存
【发布时间】:2018-03-21 02:59:19
【问题描述】:

我使用的 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 解析完成?

【问题讨论】:

  • 58,000 个承诺?!在我看来,你需要有一个切入点。如果您收到超过 X 个请求,只需放弃其中的一些。如果您获得这么多的连接,您应该考虑某种负载平衡并在多个节点进程中运行您的服务器。
  • 我怀疑这对于原生 Promise 来说是不可能的,因为他们无法知道附加的回调不会使用数据(直到 Promise 本身被 GC'd)。
  • 为了避免存储响应数据,你可以做一个 `promiseArray.push(p.then(()=>'success'))' 这应该删除每个承诺携带的数据并且仍然返回一个可用的承诺。应该。我在那里有点模糊,因此评论而不是答案。
  • 如果你不需要任何来自 Promise 的结果,为什么还要等待它们?
  • 要添加到@SethWhite 的建议,我什至不会返回成功,因为这会导致存储 58k 成功字符串,只需点击 promise resolve promiseArray.push(p.then(()=&gt; Promise.resolve()))

标签: javascript node.js bluebird


【解决方案1】:

如果您没有 58k 的 Promise、它们关联的异步操作以及它们的结果数据一次处于活动状态,那么您将使用更少的内存。

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

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

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

Bluebird 的 Promise.map() 有一个并发选项可以为您执行此操作,但也有许多方法可以同时只为运行中的 X 进行编码。

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

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

How to execute promises in series?

unable to complete promises due to out of memory

Fire off 1,000,000 requests 100 at a time

How to make it so that I can execute say 10 promises at a time in javascript to prevent rate limits on api calls?


如果您不需要解析的数据,您可以通过像这样替换它来让它更快地被 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();
    });
}

【讨论】:

  • 添加了一个控制有多少请求同时“进行中”的实现。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-09-04
  • 2010-09-26
  • 2018-12-18
相关资源
最近更新 更多