¿Hay alguna diferencia entre:
const [result1, result2] = await Promise.all([task1(), task2()]);
y
const t1 = task1();
const t2 = task2();
const result1 = await t1;
const result2 = await t2;
y
const [t1, t2] = [task1(), task2()];
const [result1, result2] = [await t1, await t2];
javascript
async-await
Oculto
fuente
fuente
Respuestas:
A los fines de esta respuesta, utilizaré algunos métodos de ejemplo:
res(ms)
es una función que toma un número entero de milisegundos y devuelve una promesa que se resuelve después de tantos milisegundos.rej(ms)
es una función que toma un número entero de milisegundos y devuelve una promesa que rechaza después de tantos milisegundos.Llamar
Ejemplo 1res
inicia el temporizador. UsarPromise.all
para esperar un puñado de demoras se resolverá una vez que todas las demoras hayan terminado, pero recuerde que se ejecutan al mismo tiempo:Mostrar fragmento de código
Esto significa que
Promise.all
se resolverá con los datos de las promesas internas después de 3 segundos.Pero
Ejemplo # 2Promise.all
tiene un comportamiento de "falla rápida" :Mostrar fragmento de código
Si lo utiliza
Ejemplo # 3async-await
, deberá esperar a que cada promesa se resuelva secuencialmente, lo que puede no ser tan eficiente:Mostrar fragmento de código
fuente
unhandledrejection
errores. Nunca querrás usar esto. Por favor agregue esto a su respuesta.Primera diferencia: falla rápido
Estoy de acuerdo con la respuesta de @ zzzzBov, pero la ventaja de "fallar rápido" de Promise.all no es solo la única diferencia. Algunos usuarios en los comentarios preguntan por qué usar Promise.all cuando solo es más rápido en un escenario negativo (cuando falla una tarea). Y pregunto por qué no. Si tengo dos tareas paralelas asíncronas independientes y la primera se resuelve en mucho tiempo, pero la segunda se rechaza en muy poco tiempo, ¿por qué dejar al usuario esperando el mensaje de error "mucho tiempo" en lugar de "muy poco tiempo"? En aplicaciones de la vida real debemos considerar un escenario negativo. Pero está bien: en esta primera diferencia, puede decidir qué alternativa usar Promise.all vs. multiple wait.
Segunda diferencia: manejo de errores
Pero al considerar el manejo de errores, DEBE usar Promise.all. No es posible manejar correctamente los errores de tareas paralelas asíncronas desencadenadas con espera múltiple. En un escenario negativo, siempre terminará con
UnhandledPromiseRejectionWarning
yPromiseRejectionHandledWarning
aunque use try / catch en cualquier lugar. Es por eso que Promise.all fue diseñado. Por supuesto, alguien podría decir que podemos suprimir esos errores usandoprocess.on('unhandledRejection', err => {})
y,process.on('rejectionHandled', err => {})
pero no es una buena práctica. Encontré muchos ejemplos en Internet que no consideran el manejo de errores para dos o más tareas paralelas asíncronas independientes o lo consideran de manera incorrecta, solo usando try / catch y esperando que detecte errores. Es casi imposible encontrar buenas prácticas. Por eso estoy escribiendo esta respuesta.Resumen
Nunca use el modo de espera múltiple para dos o más tareas paralelas asíncronas independientes porque no podrá manejar los errores en serio. Utilice siempre Promise.all () para este caso de uso. Async / await no reemplaza a Promises. Es una forma bastante bonita de usar promesas ... el código asincrónico está escrito en estilo de sincronización y podemos evitar múltiples
then
promesas.Algunas personas dicen que usando Promise.all () no podemos manejar los errores de las tareas por separado, sino solo el error de la primera promesa rechazada (sí, algunos casos de uso pueden requerir un manejo separado, por ejemplo, para el registro). No es un problema; consulte el título "Adición" a continuación.
Ejemplos
Considere esta tarea asíncrona ...
Cuando ejecuta tareas en un escenario positivo, no hay diferencia entre Promise.all y múltiples en espera. Ambos ejemplos terminan
Task 1 succeed! Task 2 succeed!
después de 5 segundos.Cuando la primera tarea demora 10 segundos en un escenario positivo y la tarea de segundos demora 5 segundos en un escenario negativo, existen diferencias en los errores emitidos.
Ya deberíamos notar aquí que estamos haciendo algo mal cuando utilizamos múltiples esperas en paralelo. Por supuesto, para evitar errores, debemos manejarlo. Intentemos...
Como puede ver para manejar con éxito el error, necesitamos agregar solo una captura para
run
funcionar y el código con lógica de captura está en devolución de llamada ( estilo asíncrono ). No necesitamos manejar errores dentro de larun
función porque la función asíncrona lo hace automáticamente: el rechazo de la promesa de promesatask
causa el rechazo de larun
función. Para evitar la devolución de llamada, podemos usar el estilo de sincronización (async / await + try / catch)try { await run(); } catch(err) { }
pero en este ejemplo no es posible porque no podemos usarloawait
en el hilo principal; solo se puede usar en la función async (es lógico porque nadie quiere) bloquear el hilo principal). Para probar si el manejo funciona en estilo de sincronización, podemos llamarrun
función desde otra función o uso asíncrono IIFE (Inmediatamente Se invoca expresión de función):(async function() { try { await run(); } catch(err) { console.log('Caught error', err); }; })();
.Esta es solo una manera correcta de cómo ejecutar dos o más tareas paralelas asíncronas y manejar errores. Debe evitar los ejemplos a continuación.
Podemos tratar de manejar el código anterior de varias maneras ...
... no se detectó nada porque maneja el código de sincronización pero
run
es asíncrono... Wtf? Primero vemos que el error para la tarea 2 no se manejó y luego se detectó. Engañoso y aún lleno de errores en la consola. Inutilizable de esta manera.
... lo mismo de arriba. El usuario @Qwerty en su respuesta eliminada preguntó sobre este extraño comportamiento que parece detectarse pero también hay errores no manejados. Capturamos el error porque run () se rechaza en línea con la palabra clave wait y se puede capturar usando try / catch al llamar a run (). También recibimos un error no controlado porque estamos llamando a la función de tarea asíncrona sincrónicamente (sin esperar palabra clave) y esta tarea se ejecuta fuera de la función run () y también falla fuera. Es similar cuando no somos capaces de manejar el error por try / catch cuando se llama a una función de sincronización de qué parte del código se ejecuta en setTimeout ...
function test() { setTimeout(function() { console.log(causesError); }, 0); }; try { test(); } catch(e) { /* this will never catch error */ }
.... "solo" dos errores (falta el tercero) pero no se detecta nada.
Adición (maneje los errores de la tarea por separado y también el error de primer fallo)
... tenga en cuenta que en este ejemplo utilicé negativeScenario = true para ambas tareas para una mejor demostración de lo que sucede (
throw err
se utiliza para disparar el error final)fuente
En general, el uso de
Promise.all()
solicitudes de ejecución "asíncrono" en paralelo. El usoawait
puede ejecutarse en paralelo O ser un bloqueo de "sincronización".Las funciones test1 y test2 a continuación muestran cómo se
await
puede ejecutar async o sync.test3 muestra
Promise.all()
que es asíncrono.jsfiddle con resultados cronometrados : abra la consola del navegador para ver los resultados de la prueba
Comportamiento de sincronización . NO se ejecuta en paralelo, tarda ~ 1800 ms :
Comportamiento asíncrono . Se ejecuta en paralelo, tarda ~ 600 ms :
Comportamiento asíncrono . Se ejecuta en paralelo, tarda ~ 600 ms :
TLDR; Si lo está utilizando
Promise.all()
, también "fallará rápidamente": dejará de ejecutarse en el momento del primer fallo de cualquiera de las funciones incluidas.fuente
Puedes comprobarlo por ti mismo.
En este violín , realicé una prueba para demostrar la naturaleza de bloqueo
await
, en contraposición a laPromise.all
cual comenzará todas las promesas y mientras una espera, continuará con las otras.fuente
t1 = task1(); t2 = task2()
y luego usarawait
para ambosresult1 = await t1; result2 = await t2;
como en su pregunta, en lugar de lo que está probando, que está usandoawait
en la llamada originalresult1 = await task1(); result2 = await task2();
. El código en su pregunta comienza todas las promesas a la vez. La diferencia, como muestra la respuesta, es que las fallas se informarán más rápido con elPromise.all
camino.En caso de esperar Promise.all ([tarea1 (), tarea2 ()]); "task1 ()" y "task2 ()" se ejecutarán en paralelo y esperarán hasta que se completen ambas promesas (resueltas o rechazadas). Considerando que en caso de
t2 solo se ejecutará después de que t1 haya finalizado la ejecución (se haya resuelto o rechazado). Tanto t1 como t2 no se ejecutarán en paralelo.
fuente