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 за раз
Если вам не нужны разрешенные данные, вы можете разрешить их 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();
});
}