Manejo de errores en Promise.all

Resuelto Jon asked hace 9 años • 22 respuestas

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.alldevuelve 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 .catchantes Promise.allde .thenparece haber servido para detectar cualquier error de las promesas originales, pero luego devolver toda la matriz al siguiente .then.

Jon avatar May 21 '15 07:05 Jon
Aceptado

Promise.alles 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
});
jib avatar May 21 '2015 15:05 jib

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.
Solominh avatar Sep 03 '2017 14:09 Solominh

ES2020 introduce un nuevo método para el Promisetipo: Promise.allSettled().

Promise.allSettledle 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']
}
Expandir fragmento

Lea más en la publicación del blog v8 .

MosheZada avatar Dec 21 '2019 13:12 MosheZada

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));
Expandir fragmento

Polirelleno

Mostrar fragmento de código

Kamil Kiełczewski avatar Jun 29 '2020 09:06 Kamil Kiełczewski

Para continuar el Promise.allciclo (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 resultsy errors.

La idea es que todas las Promesas que pase executeAllPromisesse 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, executeAllPromisesacumula todos los valores de las promesas envueltas y devuelve el objeto final con una matriz for resultsy 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}`);
});
Expandir fragmento

Benny Code avatar Jan 06 '2017 14:01 Benny Code