race new example data all javascript promise ecmascript-6 es6-promise

javascript - new - Controlador de error ES6 Promise.all(): ¿se necesita.settle()?



return new promise all (1)

¿Voy por el camino equivocado o extender las promesas ES6 con un método de liquidación es lo correcto?

No puede usar Promise.all() directamente para generar .settle() comportamiento de tipo Promise.all() que le .settle() todos los resultados, ya sea que los rechace o no, porque Promise.all() falla rápidamente y regresa tan pronto como la primera promesa rechaza y solo devuelve esa razón de rechazo, ninguno de los otros resultados.

Entonces, se necesita algo diferente. Muchas veces, la forma más sencilla de resolver ese problema es simplemente agregando un controlador .then() a cualquier operación que cree su conjunto de promesas de modo que capture cualquier rechazo y lo convierta en promesas cumplidas con algún valor específico que pueda probar. Pero, ese tipo de solución depende de la implementación, ya que depende exactamente de qué tipo de valor está devolviendo, por lo que no es del todo genérico.

Si desea una solución genérica, algo como .settle() es bastante útil.

No puedes usar la estructura:

Promise.all([...]).settle(...).then(...);

Nota (agregado en 2019): Parece que el esfuerzo de los estándares de Promise ha elegido a Promise.allSettled() como una implementación estándar del comportamiento de "asentamiento". Puedes ver más sobre eso al final de esta respuesta.

Porque Promise.all() rechaza cuando la primera promesa que usted hace rechaza y solo devuelve ese rechazo. La lógica .settle() funciona así:

Promise.settle([...]).then(...);

Y, si está interesado, aquí hay una implementación bastante simple de Promise.settle() :

// ES6 version of settle Promise.settle = function(promises) { function PromiseInspection(fulfilled, val) { return { isFulfilled: function() { return fulfilled; }, isRejected: function() { return !fulfilled; }, isPending: function() { // PromiseInspection objects created here are never pending return false; }, value: function() { if (!fulfilled) { throw new Error("Can''t call .value() on a promise that is not fulfilled"); } return val; }, reason: function() { if (fulfilled) { throw new Error("Can''t call .reason() on a promise that is fulfilled"); } return val; } }; } return Promise.all(promises.map(function(p) { // make sure any values are wrapped in a promise return Promise.resolve(p).then(function(val) { return new PromiseInspection(true, val); }, function(err) { return new PromiseInspection(false, err); }); })); }

En esta implementación, Promise.settle() siempre se resolverá (nunca rechazará) y se resuelve con una matriz de objetos PromiseInspection que le permite probar cada resultado individual para ver si se resolvió o rechazó y cuál fue el valor o la razón de cada uno. Funciona adjuntando un controlador .then() a cada promesa aprobada que maneja la resolución o el rechazo de esa promesa y coloca el resultado en un objeto PromiseInspection que luego se convierte en el valor resuelto de la promesa.

Entonces usarías esta implementación de esta manera;

Promise.settle([...]).then(function(results) { results.forEach(function(pi, index) { if (pi.isFulfilled()) { console.log("p[" + index + "] is fulfilled with value = ", pi.value()); } else { console.log("p[" + index + "] is rejected with reasons = ", pi.reason()); } }); });

Para su información, he escrito otra versión de .settle que llamo .settleVal() y, a menudo, me resulta más fácil de usar cuando no necesita el motivo de rechazo real, solo desea saber si se rechazó una ranura de matriz determinada o no. En esta versión, pasa un valor predeterminado que debe ser sustituido por cualquier promesa rechazada. Luego, solo obtiene una matriz plana de valores devueltos y cualquiera que esté establecido en el valor predeterminado donde se rechazó. Por ejemplo, a menudo puede elegir un rejectVal de null o 0 o "" o {} y hace que los resultados sean más fáciles de manejar. Aquí está la función:

// settle all promises. For rejected promises, return a specific rejectVal that is // distinguishable from your successful return values (often null or 0 or "" or {}) Promise.settleVal = function(rejectVal, promises) { return Promise.all(promises.map(function(p) { // make sure any values or foreign promises are wrapped in a promise return Promise.resolve(p).then(null, function(err) { // instead of rejection, just return the rejectVal (often null or 0 or "" or {}) return rejectVal; }); })); };

Y luego lo usas así:

Promise.settleVal(null, [...]).then(function(results) { results.forEach(function(pi, index) { if (pi !== null) { console.log("p[" + index + "] is fulfilled with value = ", pi); } }); });

Este no es un reemplazo completo de .settle() porque a veces es posible que desee saber el motivo real por el que fue rechazado o no puede distinguir fácilmente un valor rechazado de un valor no rechazado. Pero, encuentro que más del 90% del tiempo, esto es más simple de usar.

Aquí está mi última simplificación para .settle() que deja una instanceof Error en la matriz de retorno como medio para distinguir entre valores resueltos y errores rechazados:

// settle all promises. For rejected promises, leave an Error object in the returned array Promise.settleVal = function(promises) { return Promise.all(promises.map(function(p) { // make sure any values or foreign promises are wrapped in a promise return Promise.resolve(p).catch(function(err) { let returnVal = err; // instead of rejection, leave the Error object in the array as the resolved value // make sure the err is wrapped in an Error object if not already an Error object if (!(err instanceof Error)) { returnVal = new Error(); returnVal.data = err; } return returnVal; }); })); };

Y luego lo usas así:

Promise.settleVal(null, [...]).then(function(results) { results.forEach(function(item, index) { if (item instanceof Error) { console.log("p[" + index + "] rejected with error = ", item); } else { console.log("p[" + index + "] fulfilled with value = ", item); } }); });

Esto puede ser un reemplazo completo de .settle() para todos los casos, siempre y cuando una instanceof Error nunca sea un valor resuelto de sus promesas (lo que realmente no debería ser).

Esfuerzo de estándares de promesa

A partir de 2019, parece que Promise.allSettled() está convirtiendo en el estándar para este tipo de comportamiento. Y, aquí hay un polyfill:

if (!Promise.allSettled) { Promise.allSettled = function(promises) { let wrappedPromises = Array.from(promises).map(p => this.resolve(p).then( val => ({ state: ''fulfilled'', value: val }), err => ({ state: ''rejected'', reason: err }) ) ); return this.all(wrappedPromises); } }

El uso sería así:

let promises = [...]; // some array of promises, some of which may reject Promise.allSettled(promises).then(results => { for (let r of results) { if (r.state === ''fulfilled'') { console.log(''fulfilled:'', r.val); } else { console.log(''rejected:'', r.err); } } });

Tenga en cuenta que Promise.allSettled() siempre se resuelve, nunca rechaza, aunque los siguientes .then() podrían lanzar o devolver una promesa rechazada para rechazar toda la cadena.

A partir de junio de 2019, esto aún no está en el navegador Chrome de escritorio actual, pero está previsto para un próximo lanzamiento (por ejemplo, más adelante en 2019).

Esta pregunta ya tiene una respuesta aquí:

Digamos que tengo un Promise.all() que maneja dos promesas. Si una promesa produce un error, pero la otra se resuelve, me gustaría poder manejar los errores en función de la situación después de que Promise.all() haya resuelto.

A las promesas de ES6 les falta el método de liquidación, supongo que por una buena razón. Pero no puedo evitar pensar que el método .settle() me facilitaría mucho este problema.

¿Voy por el camino equivocado o extender las promesas ES6 con un método de liquidación es lo correcto?

Un ejemplo de cómo estoy pensando en usar .settle() :

Promise.all([Action1,Action2]) .settle(function(arrayOfSettledValues) //if 1 failed but not 2, handle //if 2 failed but not 1, handle //etc.... )