Tengo una matriz que contiene una variedad de promesas, y cada matriz interna podría tener 4k, 2k o 500 promesas.

En total, hay alrededor de 60k promesas y también puedo probarlo con otros valores.

Ahora necesito ejecutar el Promise.all(BigArray[0]).

Una vez que se realiza la primera matriz interna, necesito ejecutar la siguiente Promise.all(BigArray[1]) y así sucesivamente.

Si intento ejecutar un Promise.all(BigArray) está arrojando:

fatal error call_and_retry_2 allocation failed - process out of memory

Necesito ejecutar cada una de las promesas en serie, no en paralelo, lo que creo que es lo que está haciendo Node. ¡No debería usar nuevas bibliotecas, sin embargo, estoy dispuesto a considerar la respuesta!

Editar:

Aquí hay un ejemplo de código:

function getInfoForEveryInnerArgument(InnerArray) {
    const CPTPromises = _.map(InnerArray, (argument) => getDBInfo(argument));
    return Promise.all(CPTPromises)
        .then((results) => {
            return doSomethingWithResults(results);
        });
}
function mainFunction() {
    BigArray = [[argument1, argument2, argument3, argument4], [argument5, argument6, argument7, argument8], ....];
    //the summ of all arguments is over 60k...
    const promiseArrayCombination = _.map(BigArray, (InnerArray, key) => getInfoForEveryInnerArgument(InnerArray));

    Promise.all(promiseArrayCombination).then((fullResults) => {
        console.log(fullResults);
        return fullResults;
    })
}
13
user1554966 13 may. 2016 a las 17:57

4 respuestas

La mejor respuesta

Su pregunta está un poco mal nombrada, lo que puede haber confundido a algunas personas en esta pregunta y en la versión anterior de esta pregunta. Está intentando ejecutar un lote de operaciones asíncronas en serie, un lote de operaciones, luego, cuando haya terminado, ejecute otro lote de operaciones. Los resultados de esas operaciones asíncronas se rastrean con promesas. Las promesas mismas representan operaciones asincrónicas que ya se han iniciado. Las "promesas" no se ejecutan por sí mismas. Entonces, técnicamente, no "ejecuta un lote de promesas en serie". Ejecuta un conjunto de operaciones, realiza un seguimiento de sus resultados con promesas y luego ejecuta el siguiente lote cuando el primer lote está listo.

De todos modos, aquí hay una solución para serializar cada lote de operaciones.

Puede crear una función interna que generalmente llamo next() que le permite procesar cada iteración. Cuando se resuelve la promesa de procesar un Array interno, vuelve a llamar a next():

function mainFunction() {
    return new Promise(function(resolve, reject) {
        var bigArray = [[argument1, argument2, argument3, argument4], [argument5, argument6, argument7, argument8], ....];
        //the summ of all arguments is over 60k...
        var results = [];

        var index = 0;
        function next() {
            if (index < bigArray.length) {
                getInfoForEveryInnerArgument(bigArray[index++]).then(function(data) {
                    results.push(data);
                    next();
                }, reject);
            } else {
                resolve(results);
            }
        }
        // start first iteration
        next();
    });
}

Esto también recopila todos los resultados secundarios en una matriz de resultados y devuelve una promesa maestra cuyo valor resuelto es esta matriz de resultados. Entonces, podrías usar esto como:

mainFunction().then(function(results) {
    // final results array here and everything done
}, function(err) {
    // some error here
});

También puede usar el patrón de diseño .reduce() para iterar una matriz en serie:

function mainFunction() {
    var bigArray = [[argument1, argument2, argument3, argument4], [argument5, argument6, argument7, argument8], ....];
    return bigArray.reduce(function(p, item) {
        return p.then(function(results) {
            return getInfoForEveryInnerArgument(item).then(function(data) {
                results.push(data);
                return results;
            })
        });
    }, Promise.resolve([]));
}

Esto crea más promesas simultáneas que la primera opción y no sé si eso es un problema para un conjunto tan grande de promesas (es por eso que ofrecí la opción original), pero este código es más limpio y el concepto es conveniente de usar para otras situaciones también.


Para su información, hay algunas características adicionales prometedoras creadas para hacer esto por usted. En la Bluebird promise library (que es una gran biblioteca para el desarrollo mediante promesas), tienen Promise.map() que está hecho para esto:

function mainFunction() {
    var bigArray = [[argument1, argument2, argument3, argument4], [argument5, argument6, argument7, argument8], ....];
    return Promise.map(bigArray, getInfoForEveryInnerArgument);

}
10
jfriend00 13 may. 2016 a las 16:21

@ jfriend00 Simplemente agregue a su respuesta usando async/await con reduce:

function runPromisesInSeries(bigArray, getInfoForEveryInnerArgument) {
  try {
    return bigArray.reduce(async (acc, cItem) => {
      const results = await acc
      const data = await getInfoForEveryInnerArgument(cItem)
      results.push(data)
      return results
    }, Promise.resolve([]))
  } catch (err) {
    throw err
  }
}
1
hyprstack 19 oct. 2018 a las 09:19

Además, si la matriz original no es de promesas, sino de objetos que deben procesarse, el procesamiento por lotes se puede realizar sin una dependencia externa mediante la combinación de Array.prototype.map(), Array.prototype.slice() y Promise.all():

// Main batch parallelization function.
function batch(tasks, pstart, atonce, runner, pos) {
  if (!pos) pos = 0;
  if (pos >= tasks.length) return pstart;
  var p = pstart.then(function() {
    output('Batch:', pos / atonce + 1);
    return Promise.all(tasks.slice(pos, pos + atonce).map(function(task) {
      return runner(task);
    }));
  });
  return batch(tasks, p, atonce, runner, pos + atonce);
}

// Output function for the example
function output() {
  document.getElementById("result").innerHTML += Array.prototype.slice.call(arguments).join(' ') + "<br />";
  window.scrollTo(0, document.body.scrollHeight);
}

/*
 * Example code.
 * Note: Task runner should return Promise.
 */
function taskrunner(task) {
  return new Promise(function(resolve, reject) {
    setTimeout(function() {
      output('Processed:', task.text, 'Delay:', task.delay);
      resolve();
    }, task.delay);
  });
}

var taskarray = [];
function populatetasks(size) {
  taskarray = [];
  for (var i = 0; i < size; i++) {
    taskarray.push({
      delay: 500 + Math.ceil(Math.random() * 50) * 10,
      text: 'Item ' + (i + 1)
    });
  }
}

function clean() {
  document.getElementById("result").innerHTML = '';
}

var init = Promise.resolve();
function start() {
  var bsize = parseInt(document.getElementById("batchsize").value, 10),
    tsize = parseInt(document.getElementById("taskssize").value, 10);
  populatetasks(tsize);
  init = batch(taskarray.slice() /*tasks array*/ , init /*starting promise*/ , bsize /*batch size*/ , taskrunner /*task runner*/ );
}
<input type="button" onclick="start()" value="Start" />
<input type="button" onclick="clean()" value="Clear" />&nbsp;Batch size:&nbsp;
<input id="batchsize" value="4" size="2"/>&nbsp;Tasks:&nbsp;
<input id="taskssize" value="10" size="2"/>
<pre id="result" />
1
Annarfych 28 nov. 2016 a las 19:22

Puede hacerlo de forma recursiva, por ejemplo, aquí necesitaba poner alrededor de 60k documentos en mongo, pero era demasiado grande, para hacerlo en un solo paso, por lo tanto, tomo 1k documentos, los envío al mongo, una vez terminado, tomo otros documentos 1k etc.

exports.rawRecursive = (arr, start) => {
        //ending condition
        if (start > arr.length) {
            return;
        }

        Rawmedicament.insertManyAsync(_.slice(arr, start, start + 1000)).then(() => {
            //recursive
            exports.rawRecursive(arr, start + 1000);
        });
};

Si desea darse cuenta, cuando todo esté hecho, puede poner la devolución de llamada en condición final o si desea Promesas, puede llamar a resolver () allí.

0
libik 13 may. 2016 a las 15:07