Tengo una gran cantidad de datos para crear en la base de datos y quiero generarlos en paralelo con un límite de concurrencia. Para hacer eso, estoy usando el método Promise.map de bluebird con el límite de concurrency establecido. Sin embargo, también necesito detener la ejecución de la cadena tan pronto como falle una de las promesas.
Este es mi código actual:
await Promise.map( entry.tags, async (tag) => { await factory(Tag)().create(tag); }, { concurrency: 1000 }, ).catch(() => { /** @todo find a better way */ process.exit(1); }); Por el momento, como puede ver, estoy usando process.exit(1) pero no manejaría el código de salida allí. En su lugar, la cadena Promise debería detenerse instantáneamente y el error debería lanzarse para ser detectado por la función principal.
Con la biblioteca p-map , existe el parámetro "stopOnError" que funciona exactamente como espero, no puedo encontrar nada similar en Bluebird. También intenté usar el método .cancel() dentro de .catch() pero aparentemente no funciona.
Su cadena de promesa se detiene instantáneamente y generaría un error en su función de llamada. Sin embargo, dos cosas están sucediendo aquí:
.catch en su Promesa, está manejando un await que, de lo contrario, se convertiría en el error que desea.Vea este violín , ya que Stack Snippets no es compatible con Bluebird:
(async function() { try { await Promise.map( seq(100), (i) => { console.log(i); if (Math.random() > 0.5) return Promise.resolve(); else return Promise.reject(); }, { concurrency: 5 }, ).catch(() => { console.error("catch 1"); }); } catch(e) { console.error("catch 2"); } })(); function seq(i) { return Array(i).keys(); }A medida que se ejecuta el código, verá el registro de "captura 1" pero no "captura 2", porque el bloque catch hace que la promesa externa se resuelva en lugar de rechazarla . También notará que se imprimen menos de 100 números en la salida; de hecho, menos de 5 se imprimen a la salida, ya que Bluebird deja de mapear lo antes posible, y en este ejemplo, la Promesa falla lo suficientemente rápido como para que Bluebird ni siquiera tenga la oportunidad de llenar a todos los trabajadores concurrentes. En su código real, que presumiblemente es asíncrono, es posible que vea que la mayoría o todos sus 1000 trabajadores comienzan antes de que alguno de ellos falle lo suficientemente rápido como para cancelar.
Puede reducir su parámetro de concurrencia si desea que se realicen menos intentos a la vez, pero nuevamente, no hay una forma garantizada de cancelar Promises en Javascript. Incluso en Bluebird, que admite un método de cancelación de Promise , "cancelar una promesa simplemente significa que no se llamará a las devoluciones de llamada de su controlador". Esta semántica, que el documento etiqueta como semántica de "no importa" en contraste con la semántica de "abortar", es redundante para la semántica que Promise.map ya le proporciona.
Con respecto al bloque de catch : si eliminara la llamada de catch que rodea a mi "captura 1", fallaría en "captura 2" en el bloque de try externo, equivalente a detectar el error en la función principal como prefiera. Como alternativa, el bloque catch de Promise podría volver a generar el motivo de rechazo, lo que haría que tanto "captura 1" como "captura 2" se impriman en sus registros:
).catch((t) => { console.error("catch 1"); return Promise.reject(t); });