w3schools promises nodejs google from ejemplo javascript promise es6-promise

javascript - promises - Manejo de errores en Promise.all



promises javascript w3schools (13)

¿Has considerado Promise.prototype.finally() ?

Parece estar diseñado para hacer exactamente lo que desea: ejecutar una función una vez que todas las promesas se hayan resuelto (resuelto / rechazado), independientemente de que algunas de las promesas sean rechazadas.

De la documentación de MDN :

El método finally() puede ser útil si desea realizar algún procesamiento o limpieza una vez que se cumpla la promesa, independientemente de su resultado.

El método finally() es muy similar a llamar a .then(onFinally, onFinally) sin embargo, hay un par de diferencias:

Al crear una función en línea, puede pasarla una vez, en lugar de verse obligado a declararla dos veces o crear una variable para ella.

Una devolución de llamada finalmente no recibirá ningún argumento, ya que no hay medios confiables para determinar si la promesa se cumplió o rechazó. Este caso de uso es precisamente cuando no le importa el motivo del rechazo o el valor de cumplimiento, por lo que no es necesario proporcionarlo.

A diferencia de Promise.resolve(2).then(() => {}, () => {}) (que se resolverá con undefined), Promise.resolve(2).finally(() => {}) se resolverá con 2. Del mismo modo, a diferencia de Promise.reject(3).then(() => {}, () => {}) (que se cumplirá con undefined), Promise.reject(3).finally(() => {}) será rechazado con 3.

== Fallback ==

Si su versión de JavaScript no es compatible con Promise.prototype.finally() , puede usar esta solución alternativa de Jake Archibald : Promise.all(promises.map(p => p.catch(() => undefined)));

Tengo una serie de Promesas que estoy resolviendo con Promise.all (arrayOfPromises);

Continúo para continuar la cadena de promesa. Se ve algo como 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 error).

He intentado 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.

¡Gracias!

-

Editar:

Lo que dijeron las respuestas a continuación era completamente cierto, el código se estaba rompiendo debido a otras razones. En caso de que alguien esté interesado, esta es la solución con la que terminé ...

Cadena de servidor Express Node

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 el .catch para Promise.all antes del .then parece haber servido para atrapar cualquier error de las promesas originales, pero luego devolver toda la matriz al siguiente .then

¡Gracias!


Alternativamente, si tiene un caso en el que no le importan especialmente los valores de las promesas resueltas cuando hay un fallo pero aún desea que se ejecuten, puede hacer algo como esto que se resolverá con las promesas de manera normal cuando todos tienen éxito y rechazan con las promesas fallidas cuando alguno de ellos falla:

function promiseNoReallyAll (promises) { return new Promise( async (resolve, reject) => { const failedPromises = [] const successfulPromises = await Promise.all( promises.map( promise => promise.catch(error => { failedPromises.push(error) }) ) ) if (failedPromises.length) { reject(failedPromises) } else { resolve(successfulPromises) } } ) }


Así es como Promise.all está diseñado para funcionar. Si una sola promesa reject() , todo el método falla inmediatamente.

Hay casos de uso en los que uno podría desear tener Promise.all permitiendo que las promesas fracasen. Para que esto suceda, simplemente no use ninguna declaración de reject() en su promesa. Sin embargo, para garantizar que su aplicación / script no se congele en caso de que alguna promesa subyacente nunca reciba una respuesta, debe poner un tiempo de espera.

function getThing(uid,branch){ return new Promise(function (resolve, reject) { xhr.get().then(function(res) { if (res) { resolve(res); } else { resolve(null); } setTimeout(function(){reject(''timeout'')},10000) }).catch(function(error) { resolve(null); }); }); }


Como @jib dijo:

Promise.all es todo o nada.

Sin embargo, puede controlar ciertas promesas que están "permitidas" a fallar y nos gustaría proceder a .then .

Por ejemplo.

Promise.all([ doMustAsyncTask1, doMustAsyncTask2, doOptionalAsyncTask .catch(err => { if( /* err non-critical */) { return } // if critical then fail throw err }) ]) .then(([ mustRes1, mustRes2, optionalRes ]) => { // proceed to work with results })


Escribí una biblioteca npm para lidiar con este problema más hermoso. https://github.com/wenshin/promiseallend

Instalar

npm i --save promiseallend

2017-02-25 nueva api, no es romper los principios de promesa

const promiseAllEnd = require(''promiseallend''); const promises = [Promise.resolve(1), Promise.reject(''error''), Promise.resolve(2)]; const promisesObj = {k1: Promise.resolve(1), k2: Promise.reject(''error''), k3: Promise.resolve(2)}; // input promises with array promiseAllEnd(promises, { unhandledRejection(error, index) { // error is the original error which is ''error''. // index is the index of array, it''s a number. console.log(error, index); } }) // will call, data is `[1, undefined, 2]` .then(data => console.log(data)) // won''t call .catch(error => console.log(error.detail)) // input promises with object promiseAllEnd(promisesObj, { unhandledRejection(error, prop) { // error is the original error. // key is the property of object. console.log(error, prop); } }) // will call, data is `{k1: 1, k3: 2}` .then(data => console.log(data)) // won''t call .catch(error => console.log(error.detail)) // the same to `Promise.all` promiseAllEnd(promises, {requireConfig: true}) // will call, `error.detail` is ''error'', `error.key` is number 1. .catch(error => console.log(error.detail)) // requireConfig is Array promiseAllEnd(promises, {requireConfig: [false, true, false]}) // won''t call .then(data => console.log(data)) // will call, `error.detail` is ''error'', `error.key` is number 1. .catch(error => console.log(error.detail)) // requireConfig is Array promiseAllEnd(promises, {requireConfig: [true, false, false]}) // will call, data is `[1, undefined, 2]`. .then(data => console.log(data)) // won''t call .catch(error => console.log(error.detail))

————————————————————————————————

Antigua mala API, ¡no la uses!

let promiseAllEnd = require(''promiseallend''); // input promises with array promiseAllEnd([Promise.resolve(1), Promise.reject(''error''), Promise.resolve(2)]) .then(data => console.log(data)) // [1, undefined, 2] .catch(error => console.log(error.errorsByKey)) // {1: ''error''} // input promises with object promiseAllEnd({k1: Promise.resolve(1), k2: Promise.reject(''error''), k3: Promise.resolve(2)}) .then(data => console.log(data)) // {k1: 1, k3: 2} .catch(error => console.log(error.errorsByKey)) // {k2: ''error''}


Para aquellos que usan ES8 que tropiezan aquí, pueden hacer algo como lo siguiente, utilizando funciones asíncronas :

var arrayOfPromises = state.routes.map(async function(route){ try { return await route.handler.promiseHandler(); } catch(e) { // Do something to handle the error. // Errored promises will return whatever you return here (undefined if you don''t return anything). } }); var resolvedPromises = await Promise.all(arrayOfPromises);


Para continuar con el bucle Promise.all (incluso cuando una Promesa rechaza) 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 para executeAllPromises incluirán en una nueva promesa que siempre se resolverá. La nueva promesa se resuelve con una matriz que tiene 2 puntos. El primer punto contiene el valor de resolución (si lo hay) y el segundo lugar mantiene el error (si la promesa envuelta lo rechaza).

Como paso final, executeAllPromises acumula todos los valores de las promesas executeAllPromises y devuelve el objeto final con una matriz para results y una matriz para 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}`); });


Podemos manejar el rechazo a nivel de promesas individuales, de modo que cuando obtengamos los resultados en nuestra matriz de resultados, el índice de la matriz que ha sido rechazado no estará undefined . Podemos manejar esa situación según sea necesario y usar los resultados restantes.

Aquí he rechazado la primera promesa, por lo que es indefinida, pero podemos usar el resultado de la segunda promesa, que se encuentra en el índice 1.

const manyPromises = Promise.all([func1(), func2()]).then(result => { console.log(result[0]); // undefined console.log(result[1]); // func2 }); function func1() { return new Promise( (res, rej) => rej(''func1'')).catch(err => { console.log(''error handled'', err); }); } function func2() { return new Promise( (res, rej) => setTimeout(() => res(''func2''), 500) ); }


Siempre puede ajustar sus funciones de devolución de promesas de manera que detecten el error y devuelvan en su lugar un valor acordado (por ejemplo, error.message), de modo que la excepción no llegue hasta la función Promise.all y la desactive.

async function resetCache(ip) { try { const response = await axios.get(`http://${ip}/resetcache`); return response; }catch (e) { return {status: ''failure'', reason: ''e.message''}; } }


Usando Async aguarde -

aquí una función asíncrona func1 devuelve un valor resuelto, y func2 arroja un error y devuelve un valor nulo en esta situación, podemos manejarlo como queramos y devolverlo en consecuencia.

const callingFunction = async () => { const manyPromises = await Promise.all([func1(), func2()]); console.log(manyPromises); } const func1 = async () => { return ''func1'' } const func2 = async () => { try { let x; if (!x) throw "x value not present" } catch(err) { return null } } callingFunction();

La salida es - [''func1'', null]


si puede usar la biblioteca q https://github.com/kriskowal/q tiene el método q.allSettled () que puede resolver este problema, puede manejar todas las promesas dependiendo de su estado, ya sea completo o rechazado.

existingPromiseChain = existingPromiseChain.then(function() { var arrayOfPromises = state.routes.map(function(route){ return route.handler.promiseHandler(); }); return q.allSettled(arrayOfPromises) }); existingPromiseChain = existingPromiseChain.then(function(arrayResolved) { //so here you have all your promises the fulfilled and the rejected ones // you can check the state of each promise arrayResolved.forEach(function(item){ if(item.state === ''fulfilled''){ // ''rejected'' for rejected promises //do somthing } else { // do something else } }) // do stuff with my array of resolved promises, eventually ending with a res.send(); });


Promise.all es todo o nada. Se resuelve una vez que todas las promesas en la matriz se resuelven o rechazan 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 . Cuando entiendo, esperaría a que todas las promesas en la matriz se resuelvan o rechacen, pero no estoy familiarizado con eso, y no está en ES6.

Tu codigo

Estoy de acuerdo con otros aquí en que su solución debería funcionar. Debe 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.

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á mostrando y la razón por la que no está viendo ningún mensaje de error al respecto es porque esta cadena de promesa no finaliza con un final atrapar (en cuanto a lo que nos estás mostrando de todos modos).

Me tomé la libertad de descifrar la "cadena existente" de su ejemplo y terminar la cadena con una trampa. Puede que esto no sea adecuado para usted, pero para las personas que leen esto, es importante siempre devolver o terminar las cadenas, o los posibles errores, incluso los errores de codificación, se ocultará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 FUTURE Promise

ANTIGUA RESPUESTA

Tenemos que escribir Promise.all () personalizado. Aquí la solución que uso en mi proyecto. El error será devuelto como resultado normal. Después de que finalicen todas las promesas, podemos filtrar el error.

const Promise_all = promises => { return new Promise((resolve, reject) => { const results = []; let count = 0; promises.forEach((promise, idx) => { promise .catch(err => { return err; }) .then(valueOrError => { results[idx] = valueOrError; count += 1; if (count === promises.length) resolve(results); }); }); }); }; const results = await Promise_all(promises) const validResults = results.filter(result => !(result instanceof Error));