Promise.all consume toda mi RAM

Resuelto kentor asked hace 6 años • 1 respuestas

Tengo un limitador de velocidad para una API que estoy usando y que permite 20 solicitudes por segundo. Todas las solicitudes se basan en promesas y la promesa se resolverá con los datos de la API una vez que haya una respuesta.

El problema:

Configuré un promesaArray que contiene 58k promesas, todas esperando una respuesta. Poco a poco la memoria va aumentando hasta que me quedo sin memoria. En mi situación específica, no necesito pasar los datos resueltos a mi then()y los datos están consumiendo toda mi RAM.

El código:

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

Entonces, ¿hay alguna manera de esperar hasta que se resuelva el promesaArray sin necesidad de los datos resueltos?

kentor avatar Oct 10 '17 03:10 kentor
Aceptado

Utilizará una cantidad menor de memoria si nunca tiene promesas de 58k, sus operaciones asíncronas asociadas y sus datos de resultados activos a la vez.

En su lugar, desea ejecutar X operaciones a la vez y luego, cuando una finaliza, comienza la siguiente con nunca más de X en vuelo al mismo tiempo y nunca más de X promesas en uso a la vez.

Puede experimentar con un valor apropiado de X. Un valor de 1 son operaciones secuenciales, pero a menudo puede mejorar el tiempo general de operación de un extremo a otro utilizando un valor más alto de X. Si todas las solicitudes llegan al mismo host, entonces X es probablemente no más de 5 a 10 (ya que un host determinado no puede hacer muchas cosas a la vez y pedirle que haga más de las que puede hacer a la vez solo lo ralentiza).

Si cada solicitud es a un host diferente, es posible que pueda aumentar X. La experimentación le brindaría un valor óptimo tanto para el uso máximo de memoria como para el rendimiento general y, en cierta medida, depende de sus circunstancias específicas.

Bluebird Promise.map()tiene una opción de concurrencia que hará esto por usted, pero también hay numerosas formas de codificar solo X en vuelo al mismo tiempo.

A continuación se muestran algunos otros ejemplos de codificación sobre cómo gestionar cuántos están en vuelo a la vez:

Realice varias solicitudes a una API que solo puede manejar 20 solicitudes por minuto

¿Cómo ejecutar promesas en serie?

No se pueden completar las promesas debido a falta de memoria.

Realice 1.000.000 de solicitudes, 100 a la vez

¿Cómo hacer que pueda ejecutar, digamos, 10 promesas a la vez en javascript para evitar límites de velocidad en las llamadas a la API?


Si no necesita los datos resueltos, puede permitir que se realicen GC antes reemplazándolos de esta manera:

  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)    

Y aquí hay una implementación simple que itera una matriz con no más de X solicitudes en curso al mismo tiempo:

// 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();
    });
}
jfriend00 avatar Oct 09 '2017 20:10 jfriend00