Promise.all потребляет всю мою оперативную память

У меня есть ограничитель скорости для API, который я использую, который позволяет 20 запросов в секунду. Все запросы основаны на обещаниях, и обещание будет обработано данными API после получения ответа.

Эта проблема:

Я установил блок обещаний, который содержит 58 тыс. Обещаний, ожидающих ответа. Так медленно память увеличивается, пока у меня не заканчивается память. В моей конкретной ситуации мне не нужно передавать разрешенные данные моему then() и данные поглощают всю мою оперативную память.

Код:

  }).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(() => {

Так есть ли способ ждать, пока обещание массива обещания не будет разрешено без необходимости в разрешенных данных?

1 ответ

Решение

Вы будете использовать меньший объем памяти, если у вас никогда не будет 58k обещаний, связанных с ними асинхронных операций и их результатов, активных одновременно.

Вместо этого вы хотите выполнить операции X сразу, а затем, когда одна из них завершится, вы начнете следующую, когда в полете одновременно не будет больше X и никогда не будет больше, чем X обещает использовать сразу.

Вы можете поэкспериментировать с подходящим значением X. Значение 1 - это последовательные операции, но вы часто можете улучшить общее время сквозной работы, используя более высокое значение X. Если все запросы попадают на один и тот же хост, то X вероятно, не более 5-10 (так как конкретный хост не может действительно сделать много вещей одновременно, и запрос его сделать больше, чем он может сделать одновременно, просто замедляет его).

Если каждый запрос направлен на другой хост, вы можете сделать X выше. Экспериментирование даст вам оптимальное значение как для пикового использования памяти, так и для общей пропускной способности, и в некоторой степени зависит от ваших конкретных обстоятельств.

Bluebird-х Promise.map() есть опция параллелизма, которая сделает это за вас, но есть также множество способов кодирования только для X в полете одновременно.

Вот некоторые другие примеры кодирования управления количеством во время полета:

Сделайте несколько запросов к API, который может обрабатывать только 20 запросов в минуту

Как выполнить обещания в серии?

не в состоянии выполнить обещания из-за нехватки памяти

Выстрелить 1000000 запросов по 100 за раз

Как сделать так, чтобы я мог выполнять, скажем, 10 обещаний за раз в javascript, чтобы предотвратить ограничение скорости вызовов 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();
    });
}
Другие вопросы по тегам