Manejo de errores en Promise.all
Tengo una serie de Promesas con las que estoy resolviendoPromise.all(arrayOfPromises);
Continúo con la cadena de promesas. Se parece a esto
existingPromiseChain = existingPromiseChain.then(function() {
var arrayOfPromises = state.routes.map(function(route){
return route.handler.promiseHandler();
});
return Promise.all(arrayOfPromises)
});
existingPromiseChain = existingPromiseChain.then(function(arrayResolved) {
// do stuff with my array of resolved promises, eventually ending with a res.send();
});
Quiero agregar una declaración catch para manejar una promesa individual en caso de que se produzca un error, pero cuando lo intento, Promise.all
devuelve el primer error que encuentra (ignora el resto) y luego no puedo obtener los datos del resto de las promesas en la matriz (que no dio error).
Intenté hacer algo como ...
existingPromiseChain = existingPromiseChain.then(function() {
var arrayOfPromises = state.routes.map(function(route){
return route.handler.promiseHandler()
.then(function(data) {
return data;
})
.catch(function(err) {
return err
});
});
return Promise.all(arrayOfPromises)
});
existingPromiseChain = existingPromiseChain.then(function(arrayResolved) {
// do stuff with my array of resolved promises, eventually ending with a res.send();
});
Pero eso no se resuelve.
--
Editar:
Lo que decían las respuestas a continuación era completamente cierto, el código se estaba rompiendo por otras razones. Por si a alguien le interesa, esta es la solución con la que terminé...
Cadena de servidores Node Express
serverSidePromiseChain
.then(function(AppRouter) {
var arrayOfPromises = state.routes.map(function(route) {
return route.async();
});
Promise.all(arrayOfPromises)
.catch(function(err) {
// log that I have an error, return the entire array;
console.log('A promise failed to resolve', err);
return arrayOfPromises;
})
.then(function(arrayOfPromises) {
// full array of resolved promises;
})
};
Llamada API (llamada route.async)
return async()
.then(function(result) {
// dispatch a success
return result;
})
.catch(function(err) {
// dispatch a failure and throw error
throw err;
});
Poner for .catch
antes Promise.all
de .then
parece haber servido para detectar cualquier error de las promesas originales, pero luego devolver toda la matriz al siguiente .then
.
Promise.all
es todo o nada. Se resuelve una vez que se resuelven todas las promesas de la matriz, o se rechaza tan pronto como una de ellas rechaza. En otras palabras, se resuelve con una matriz de todos los valores resueltos o se rechaza con un solo error.
Algunas bibliotecas tienen algo llamado Promise.when
, que entiendo que esperaría a que todas las promesas en la matriz se resuelvan o rechacen, pero no estoy familiarizado con ello y no está en ES6.
Tu codigo
Estoy de acuerdo con otros aquí en que su solución debería funcionar. Debería resolverse con una matriz que puede contener una combinación de valores exitosos y objetos de error. Es inusual pasar objetos de error en la ruta de éxito, pero suponiendo que su código los esté esperando, no veo ningún problema en ello.
La única razón por la que puedo pensar por qué "no se resolvería" es que está fallando en el código que no nos estás mostrando y la razón por la que no ves ningún mensaje de error sobre esto es porque esta cadena de promesa no finaliza con un final. captura (en lo que respecta a lo que nos estás mostrando de todos modos).
Me tomé la libertad de factorizar la "cadena existente" de su ejemplo y terminar la cadena con un cierre. Puede que esto no sea adecuado para usted, pero para las personas que leen esto, es importante devolver o terminar siempre las cadenas, o se ocultarán posibles errores, incluso errores de codificación (que es lo que sospecho que sucedió aquí):
Promise.all(state.routes.map(function(route) {
return route.handler.promiseHandler().catch(function(err) {
return err;
});
}))
.then(function(arrayOfValuesOrErrors) {
// handling of my array containing values and/or errors.
})
.catch(function(err) {
console.log(err.message); // some coding error in handling happened
});
NUEVA RESPUESTA
const results = await Promise.all(promises.map(p => p.catch(e => e)));
const validResults = results.filter(result => !(result instanceof Error));
API de promesa FUTURA
- Chrome 76: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise/allSettled
- Puede descargar https://www.npmjs.com/package/promise.allsettled para obtenerlo ahora. En ciertos navegadores, allSettled viene preinstalado con el propio navegador. Vale la pena descargar el paquete para su tranquilidad porque, por ejemplo. TypeScript no tiene definiciones predeterminadas para allSettled.
ES2020 introduce un nuevo método para el Promise
tipo: Promise.allSettled()
.
Promise.allSettled
le da una señal cuando se liquidan todas las promesas de entrada, lo que significa que se cumplen o se rechazan. Esto es útil en los casos en los que no le importa el estado de la promesa, solo desea saber cuándo finaliza el trabajo, independientemente de si fue exitoso.
async function() {
const promises = [
fetch('/api.stackexchange.com/2.2'), // succeeds
fetch('/this-will-fail') // fails
];
const result = await Promise.allSettled(promises);
console.log(result.map(promise => promise.status));
// ['fulfilled', 'rejected']
}
Lea más en la publicación del blog v8 .
Promesa.todo resuelto
En lugar de Promise.all, utilice Promise.allSettled , que espera a que se liquiden todas las promesas, independientemente del resultado.
let p1 = new Promise(resolve => resolve("result1"));
let p2 = new Promise( (resolve,reject) => reject('some troubles') );
let p3 = new Promise(resolve => resolve("result3"));
// It returns info about each promise status and value
Promise.allSettled([p1,p2,p3]).then(result=> console.log(result));
Polirelleno
Mostrar fragmento de código
Para continuar el Promise.all
ciclo (incluso cuando se rechaza una Promesa), escribí una función de utilidad que se llama executeAllPromises
. Esta función de utilidad devuelve un objeto con results
y errors
.
La idea es que todas las Promesas que pase executeAllPromises
se envolverán en una nueva Promesa que siempre se resolverá. La nueva Promesa se resuelve con una matriz que tiene 2 puntos. El primer lugar contiene el valor de resolución (si lo hay) y el segundo lugar mantiene el error (si la Promesa envuelta se rechaza).
Como paso final, executeAllPromises
acumula todos los valores de las promesas envueltas y devuelve el objeto final con una matriz for results
y una matriz for errors
.
Aquí está el código:
function executeAllPromises(promises) {
// Wrap all Promises in a Promise that will always "resolve"
var resolvingPromises = promises.map(function(promise) {
return new Promise(function(resolve) {
var payload = new Array(2);
promise.then(function(result) {
payload[0] = result;
})
.catch(function(error) {
payload[1] = error;
})
.then(function() {
/*
* The wrapped Promise returns an array:
* The first position in the array holds the result (if any)
* The second position in the array holds the error (if any)
*/
resolve(payload);
});
});
});
var errors = [];
var results = [];
// Execute all wrapped Promises
return Promise.all(resolvingPromises)
.then(function(items) {
items.forEach(function(payload) {
if (payload[1]) {
errors.push(payload[1]);
} else {
results.push(payload[0]);
}
});
return {
errors: errors,
results: results
};
});
}
var myPromises = [
Promise.resolve(1),
Promise.resolve(2),
Promise.reject(new Error('3')),
Promise.resolve(4),
Promise.reject(new Error('5'))
];
executeAllPromises(myPromises).then(function(items) {
// Result
var errors = items.errors.map(function(error) {
return error.message
}).join(',');
var results = items.results.join(',');
console.log(`Executed all ${myPromises.length} Promises:`);
console.log(`— ${items.results.length} Promises were successful: ${results}`);
console.log(`— ${items.errors.length} Promises failed: ${errors}`);
});