Digamos que tengo un conjunto de Promise
correos electrónicos que están haciendo solicitudes de red, de los cuales uno fallará:
// http://does-not-exist will throw a TypeError
var arr = [ fetch('index.html'), fetch('http://does-not-exist') ]
Promise.all(arr)
.then(res => console.log('success', res))
.catch(err => console.log('error', err)) // This is executed
Digamos que quiero esperar hasta que todo esto haya terminado, independientemente de si uno ha fallado. Puede haber un error de red para un recurso sin el cual puedo vivir, pero que si puedo obtener, quiero antes de continuar. Quiero manejar fallas de red con gracia.
Como Promises.all
no deja espacio para esto, ¿cuál es el patrón recomendado para manejar esto, sin usar una biblioteca de promesas?
javascript
promise
es6-promise
Nathan Hagen
fuente
fuente
allSettled
que satisface su necesidad sin que tenga que rodar la suya.Promise.all
rechazará tan pronto como cualquier promesa lo rechace, por lo que su modismo propuesto no garantiza que todas las promesas se resuelvan.Respuestas:
Actualización, probablemente desee utilizar el nativo incorporado
Promise.allSettled
:Como dato curioso, esta respuesta a continuación fue una técnica anterior al agregar ese método al lenguaje:]
Claro, solo necesitas un
reflect
:O con ES5:
O en tu ejemplo:
fuente
reflect
una palabra común en informática? ¿Puedes vincular a donde se explica esto como en wikipedia o algo así? Estaba buscando muchoPromise.all not even first reject
pero no sabía buscar "Reflexionar". ¿Debería ES6 tener unPromise.reflect
que sea como "Promise.all pero realmente todos"?Respuesta similar, pero más idiomática para ES6 quizás:
Dependiendo del tipo o tipos de valores devueltos, los errores a menudo se pueden distinguir con la suficiente facilidad (por ejemplo, usar
undefined
para "no importa",typeof
para valores simples que no sean objetosresult.message
,result.toString().startsWith("Error:")
etc.)fuente
.map(p => p.catch(e => e))
parte convierte todos los rechazos en valores resueltos, por lo quePromise.all
aún espera a que todo termine si las funciones individuales se resuelven o rechazan, independientemente del tiempo que tarden. Intentalo..catch(e => console.log(e));
nunca se llama porque esto nunca fallacatch
es generalmente una buena práctica en mi humilde opinión .e
y lo devuelve como un valor regular ( correcto ). Igual quep.catch(function(e) { return e; })
solo más corto.return
Es implícito.La respuesta de Benjamin ofrece una gran abstracción para resolver este problema, pero esperaba una solución menos abstracta. La forma explícita de resolver este problema es simplemente recurrir
.catch
a las promesas internas y devolver el error de su devolución de llamada.Dando un paso más allá, podría escribir un controlador genérico de captura que se vea así:
entonces puedes hacer
El problema con esto es que los valores capturados tendrán una interfaz diferente a los valores no capturados, por lo que para limpiar esto puede hacer algo como:
Entonces ahora puedes hacer esto:
Luego, para mantenerlo SECO, obtienes la respuesta de Benjamin:
donde ahora parece
Los beneficios de la segunda solución son su abstracción y SECO. La desventaja es que tiene más código y debe recordar reflejar todas sus promesas para hacer que las cosas sean consistentes.
Caracterizaría mi solución como explícita y KISS, pero de hecho menos robusta. La interfaz no garantiza que sepa exactamente si la promesa tuvo éxito o no.
Por ejemplo, puede tener esto:
Esto no será atrapado
a.catch
, así queNo hay forma de saber cuál fue fatal y cuál no. Si eso es importante, entonces querrá aplicar una interfaz que rastree si fue exitosa o no (lo que
reflect
sí ocurre).Si solo desea manejar los errores con gracia, puede tratar los errores como valores indefinidos:
En mi caso, no necesito saber el error o cómo falló, solo me importa si tengo el valor o no. Dejaré que la función que genera la promesa se preocupe por registrar el error específico.
De esa manera, el resto de la aplicación puede ignorar su error si lo desea, y tratarlo como un valor indefinido si lo desea.
Quiero que mis funciones de alto nivel fallen con seguridad y no me preocupe por los detalles de por qué fallaron sus dependencias, y también prefiero KISS a DRY cuando tengo que hacer ese intercambio, que es en última instancia por lo que opté por no usarlo
reflect
.fuente
Promise
s. Si bienreflect
mejora la reutilización del código, también establece otro nivel de abstracción. Como la respuesta de Nathan hasta ahora solo ha recibido una fracción de votos positivos en comparación con la suya, me pregunto si esto es una indicación de un problema con su solución, que aún no he reconocido.new Promise((res, rej) => res(new Error('Legitimate error'))
¿no sería distinguible denew Promise(((res, rej) => rej(new Error('Illegitimate error'))
? O, además, ¿no podrías filtrarx.status
? Agregaré este punto a mi respuesta para que la diferencia sea más claraPromise.all()
variante específica , y luego incumbe al consumidor de Promise saber que una promesa específica no rechazará, pero tragar es errores. De hecho, elreflect()
método podría hacerse menos "abstracto" y más explícito llamándoloPromiseEvery(promises).then(...)
. La complejidad de la respuesta anterior en comparación con la de Benjamin debería decir mucho sobre esta solución.Hay una propuesta final para una función que puede lograr esto de forma nativa, en Javascript vainilla:
Promise.allSettled
que ha llegado a la etapa 4, está oficializada en ES2020 y se implementa en todos los entornos modernos . Es muy similar a lareflect
función en esta otra respuesta . Aquí hay un ejemplo, de la página de propuesta. Antes, habrías tenido que hacer:Usando en su
Promise.allSettled
lugar, lo anterior será equivalente a:Aquellos que usan entornos modernos podrán usar este método sin ninguna biblioteca . En esos, el siguiente fragmento debe ejecutarse sin problemas:
Salida:
Para los navegadores más antiguos, hay una especificación polyfill compatible aquí .
fuente
Realmente me gusta la respuesta de Benjamin, y cómo él básicamente convierte todas las promesas en siempre resueltas, pero a veces con errores como resultado. :)
Aquí está mi intento de su solicitud en caso de que estuviera buscando alternativas. Este método simplemente trata los errores como resultados válidos y se codifica de forma similar a lo
Promise.all
contrario:fuente
settle
. También tenemos eso en bluebird, me gusta reflexionar mejor, pero esta es una solución viable para cuando tienes esto para una matriz.Promise
constructor correctamente (y evitar esavar resolve
cosita)?Se
Promise.all
tragará cualquier promesa rechazada y almacenará el error en una variable, por lo que volverá cuando se hayan resuelto todas las promesas. Luego puede volver a tirar el error o hacer lo que sea. De esta manera, supongo que sacarías el último rechazo en lugar del primero.fuente
err.push(error)
, por lo que todos los errores podrían aparecer.Tuve el mismo problema y lo resolví de la siguiente manera:
En ese caso
Promise.all
esperará a que se cumplan todas las Promesasresolved
orejected
estados.Y teniendo esta solución, estamos "deteniendo la
catch
ejecución" de una manera no bloqueante. De hecho, no detenemos nada, solo regresamos alPromise
estado pendiente que devuelve otroPromise
cuando se resuelve después del tiempo de espera.fuente
Promise.all
. Estoy buscando una forma de escuchar cuando se han invocado todas las promesas, pero no invocarlas yo mismo. Gracias.all()
hace eso, espera el cumplimiento de todas las promesas o el rechazo de al menos una de ellas..all
todo se dispara.then
o una.all
llamada) pero se ejecutan cuando se crean.Esto debería ser coherente con cómo lo hace Q :
fuente
La respuesta de Benjamin Gruenbaum es, por supuesto, genial. Pero también puedo ver si el punto de vista de Nathan Hagen con el nivel de abstracción parece vago. Tener propiedades de objeto cortas como
e & v
tampoco ayuda, pero por supuesto eso podría cambiarse.En Javascript hay un objeto de error estándar, llamado
Error
,. Idealmente, siempre arrojas una instancia / descendiente de esto. La ventaja es que puedes hacerinstanceof Error
, y sabes que algo es un error.Entonces, usando esta idea, aquí está mi opinión sobre el problema.
Básicamente, detecte el error, si el error no es del tipo Error, ajuste el error dentro de un objeto Error. La matriz resultante tendrá valores resueltos u objetos de error que puede verificar.
La instancia de dentro de la captura, es en caso de que use alguna biblioteca externa que tal vez lo hizo
reject("error")
, en lugar dereject(new Error("error"))
.Por supuesto, podría tener promesas si resuelve un error, pero en ese caso lo más probable es que tenga sentido tratarlo como un error, como muestra el último ejemplo.
Otra ventaja de hacerlo es que la destrucción de la matriz se mantiene simple.
En vez de
Podría argumentar que la
!error1
verificación es más simple que una instancia de, pero también tiene que destruir ambosv & e
.fuente
En lugar de rechazar, resuélvelo con un objeto. Podrías hacer algo como esto cuando estás implementando promesa
fuente
Creo que los siguientes ofrece un enfoque ligeramente diferente ... Comparar
fn_fast_fail()
confn_slow_fail()
... aunque este último no falla como tal ... se puede comprobar si una o las dosa
yb
es una instancia deError
ythrow
queError
si quiero que se alcance Elcatch
bloque (por ejemploif (b instanceof Error) { throw b; }
). Ver el jsfiddle .fuente
Aquí está mi costumbre
settledPromiseAll()
Comparado con
Promise.all
Si se resuelven todas las promesas, funciona exactamente como la estándar.
Si se rechaza una o más promesas, devuelve la primera rechazada de manera muy similar a la estándar pero, a diferencia de esto, espera que todas las promesas se resuelvan / rechacen.
Para los valientes podríamos cambiar
Promise.all()
:CUIDADO . En general, nunca cambiamos los elementos integrados, ya que podría romper otras bibliotecas JS no relacionadas o chocar con futuros cambios en los estándares JS.
My
settledPromiseall
es compatible con versiones anterioresPromise.all
y extiende su funcionalidad.Personas que están desarrollando estándares: ¿por qué no incluir esto en un nuevo estándar de Promise?
fuente
Promise.all
con el uso de unasync/await
enfoque modernofuente
Yo lo haría:
fuente
Puede ejecutar su lógica secuencialmente a través del ejecutor síncrono nsynjs . Pausará cada promesa, esperará la resolución / rechazo y asignará el resultado de la resolución a la
data
propiedad, o lanzará una excepción (para el manejo que necesitará try / catch block). Aquí hay un ejemplo:fuente
He estado usando los siguientes códigos desde ES5.
La firma de uso es igual
Promise.all
. La principal diferencia es quePromise.wait
esperará a que todas las promesas terminen sus trabajos.fuente
Sé que esta pregunta tiene muchas respuestas, y estoy seguro de que (si no todas) deben ser correctas. Sin embargo, fue muy difícil para mí entender la lógica / flujo de estas respuestas.
Así que miré la Implementación original
Promise.all()
e intenté imitar esa lógica, con la excepción de no detener la ejecución si fallaba una Promesa.Explicación:
- Pase sobre la entrada
promisesList
y ejecute cada Promesa.- No importa si la Promesa se resolvió o rechazó: guarde el resultado de la Promesa en una
result
matriz de acuerdo conindex
. Guarde también el estado de resolución / rechazo (isSuccess
).- Una vez completadas todas las promesas, devuelve una promesa con el resultado de todas las demás.
Ejemplo de uso:
fuente
Promise.all
, hay muchas cosas que saldrán mal. Su versión no maneja entradas vacías, por ejemplo.No sé qué biblioteca de promesas está utilizando, pero la mayoría tiene algo como allSettled .
Editar: Ok, ya que desea usar ES6 simple sin bibliotecas externas, no existe tal método.
En otras palabras: debe realizar un bucle de sus promesas manualmente y resolver una nueva promesa combinada tan pronto como se resuelvan todas las promesas.
fuente