w3schools style img attribute javascript promise async-await es2017

javascript - style - title html



Esperando más de una operación concurrente en espera (4)

Resuelve en lugar de promesas

const wait = (ms, data) => new Promise( resolve => setTimeout(resolve, ms, data) ) const reject = (ms, data) => new Promise( (r, reject) => setTimeout(reject, ms, data) ) const e = e => ''err:'' + e const l = l => (console.log(l), l) ;(async function parallel() { let task1 = reject(500, ''parallelTask1'').catch(e).then(l) let task2 = wait(2500, ''parallelTask2'').catch(e).then(l) let task3 = reject(1500, ''parallelTask3'').catch(e).then(l) console.log(''WAITING'') ;[task1, task2, task3] = [await task1, await task2, await task3] console.log(''FINISHED'', task1, task2, task3) })()

Como se señaló en otras respuestas, una promesa rechazada podría generar una excepción no controlada.
Este .catch(e => e) es un pequeño truco que atrapa el error y lo pasa por la cadena, permitiendo que la promesa se resolve , en lugar de rejecting .

Si encuentra este código ES6 feo, vea más amigable here .

¿Cómo puedo cambiar el siguiente código para que ambas operaciones asíncronas se activen y tengan la oportunidad de ejecutarse simultáneamente?

const value1 = await getValue1Async(); const value2 = await getValue2Async(); // use both values

¿Necesito hacer algo como esto?

const p1 = getValue1Async(); const p2 = getValue2Async(); const value1 = await p1; const value2 = await p2; // use both values


Use .catch () y Promise.all ()

Asegúrese de manejar los rechazos correctamente y puede usar Promises.all () sin enfrentarse a rechazos no controlados. (Editar: aclaración por discusión: no el unhandled rejection del error, sino simplemente rechazos que no están siendo manejados por el código. Promise.all() arrojará el primer rechazo de la promesa e ignorará el resto).

En el siguiente ejemplo, se devuelve una matriz de [[error, resultados], ...] para facilitar el procesamiento de resultados y / o errores.

let myTimeout = (ms, is_ok) => new Promise((resolve, reject) => setTimeout(_=> is_ok ? resolve(`ok in ${ms}`) : reject(`error in ${ms}`), ms)); let handleRejection = promise => promise .then((...r) => [null, ...r]) .catch(e => [e]); (async _=> { let res = await Promise.all([ myTimeout(100, true), myTimeout(200, false), myTimeout(300, true), myTimeout(400, false) ].map(handleRejection)); console.log(res); })();

Sin embargo, puede lanzar desde dentro de un catch () para dejar de esperar a todos (y descartar los resultados del resto): solo puede hacerlo una vez por bloques try / catch, por lo que se debe mantener y verificar una bandera has_thorwn para asegurarse No se producen errores no controlados.

let myTimeout = (ms, is_ok) => new Promise((resolve, reject) => setTimeout(_=> is_ok ? resolve(`ok in ${ms}`) : reject(`error in ${ms}`), ms)); let has_thrown = false; let handleRejection = promise => promise .then((...r) => [null, ...r]) .catch(e => { if (has_thrown) { console.log(''not throwing'', e); } else { has_thrown = 1; throw e; } }); (async _=> { try { let res = await Promise.all([ myTimeout(100, true), myTimeout(200, false), myTimeout(300, true), myTimeout(400, false) ].map(handleRejection)); console.log(res); } catch(e) { console.log(e); } console.log(''we are done''); })();


TL; DR

No use el patrón en la pregunta de dónde obtiene las promesas, y luego espere por separado; en su lugar, use Promise.all (al menos por ahora):

const [value1, value2] = await Promise.all([getValue1Async(), getValue2Async()]);

Si bien su solución ejecuta las dos operaciones en paralelo, no maneja el rechazo correctamente si ambas promesas lo rechazan.

Detalles:

Su solución los ejecuta en paralelo, pero siempre espera a que termine el primero antes de esperar al segundo. Si solo desea iniciarlos, ejecutarlos en paralelo y obtener ambos resultados, está bien. (No, no lo es, sigue leyendo ...) Ten en cuenta que si el primero tarda (digamos) cinco segundos en completarse y el segundo falla en un segundo, tu código esperará los cinco segundos completos antes de fallar.

Lamentablemente, actualmente no hay una sintaxis de await para hacer una espera paralela, por lo que tiene la incomodidad que enumeró o Promise.all . (Sin embargo, se ha debatido sobre await.all o similar ; tal vez algún día).

La versión de Promise.all es:

const [value1, value2] = await Promise.all([getValue1Async(), getValue2Async()]);

... que es más conciso, y tampoco espera a que se complete la primera operación si la segunda falla rápidamente (por ejemplo, en mi ejemplo anterior de cinco segundos / un segundo, lo anterior se rechazará en un segundo en lugar de esperar cinco) . También tenga en cuenta que con su código original, si la segunda promesa se rechaza antes de que se resuelva la primera promesa, es posible que obtenga un error de "rechazo no controlado" en la consola (actualmente lo hace con Chrome v61), aunque ese error es posiblemente falso (porque hacer , eventualmente, manejar el rechazo). Pero si ambas promesas se rechazan, obtendrá un verdadero error de rechazo no controlado porque el flujo de control nunca alcanza el valor const value2 = await p2; y así el rechazo de p2 nunca se maneja.

Los rechazos no controlados son una mala cosa (tanto que pronto, NodeJS abortará el proceso en rechazos verdaderamente no controlados, al igual que las excepciones no controladas, porque eso es lo que son), así que es mejor evitar el patrón de "obtener la promesa y luego await ". en tu pregunta

Aquí hay un ejemplo de la diferencia en el tiempo en el caso de falla (usando 500ms y 100ms en lugar de 5 segundos y 1 segundo), y posiblemente también el error de rechazo no manejable y falso (abra la consola del navegador real para verlo):

const getValue1Async = () => { return new Promise(resolve => { setTimeout(resolve, 500, "value1"); }); }; const getValue2Async = () => { return new Promise((resolve, reject) => { setTimeout(reject, 100, "error"); }); }; // This waits the full 500ms before failing, because it waits // on p1, then on p2 (async () => { try { console.time("separate"); const p1 = getValue1Async(); const p2 = getValue2Async(); const value1 = await p1; const value2 = await p2; } catch (e) { console.error(e); } console.timeEnd("separate"); })(); // This fails after just 100ms, because it doesn''t wait for p1 // to finish first, it rejects as soon as p2 rejects setTimeout(async () => { try { console.time("Promise.all"); const [value1, value2] = await Promise.all([getValue1Async(), getValue2Async()]); } catch (e) { console.timeEnd("Promise.all", e); } }, 1000);

Open the real browser console to see the unhandled rejection error.

Y aquí rechazamos tanto p1 como p2 , lo que resulta en un error de rechazo no falso no controlado en p2 :

const getValue1Async = () => { return new Promise((resolve, reject) => { setTimeout(reject, 500, "error1"); }); }; const getValue2Async = () => { return new Promise((resolve, reject) => { setTimeout(reject, 100, "error2"); }); }; // This waits the full 500ms before failing, because it waits // on p1, then on p2 (async () => { try { console.time("separate"); const p1 = getValue1Async(); const p2 = getValue2Async(); const value1 = await p1; const value2 = await p2; } catch (e) { console.error(e); } console.timeEnd("separate"); })(); // This fails after just 100ms, because it doesn''t wait for p1 // to finish first, it rejects as soon as p2 rejects setTimeout(async () => { try { console.time("Promise.all"); const [value1, value2] = await Promise.all([getValue1Async(), getValue2Async()]); } catch (e) { console.timeEnd("Promise.all", e); } }, 1000);

Open the real browser console to see the unhandled rejection error.

En un comentario que has preguntado:

Pregunta secundaria: ¿la siguiente fuerza que espera a ambos (y descarta los resultados) await p1 && await p2 ?

Esto tiene los mismos problemas relacionados con el rechazo de promesas que su código original: esperará hasta que p1 resuelva, incluso si p2 rechaza antes; puede generar un error de rechazo no manejable espurio si p2 rechaza antes de que p1 resuelva; y genera un error de rechazo genuino no manejado si tanto p1 como p2 rechazan (porque el rechazo de p2 nunca se maneja).

Este es el caso donde p1 resuelve y p2 rechaza:

const getValue1Async = () => { return new Promise(resolve => { setTimeout(resolve, 500, false); }); }; const getValue2Async = () => { return new Promise((resolve, reject) => { setTimeout(reject, 100, "error"); }); }; (async () => { try { const p1 = getValue1Async(); const p2 = getValue2Async(); console.log("waiting"); await p1 && await p2; } catch (e) { console.error(e); } console.log("done waiting"); })();

Look in the real console (for the unhandled rejection error).

... y donde ambos rechazan:

const getValue1Async = () => { return new Promise((resolve, reject) => { setTimeout(reject, 500, "error1"); }); }; const getValue2Async = () => { return new Promise((resolve, reject) => { setTimeout(reject, 100, "error2"); }); }; (async () => { try { const p1 = getValue1Async(); const p2 = getValue2Async(); console.log("waiting"); await p1 && await p2; } catch (e) { console.error(e); } console.log("done waiting"); })();

Look in the real console (for the unhandled rejection error).


Creo que esto debería funcionar:

const [value1, value2] = await Promise.all([getValue1Async(),getValue2Async()]);

Un ejemplo más detallado está a continuación en caso de que ayude a comprender:

const promise1 = async() => { return 3; } const promise2 = async() => { return 42; } const promise3 = async() => { return 500; // emulate an error // throw "something went wrong..."; } const f1 = async() => { try { // returns an array of values const results = await Promise.all([promise1(), promise2(), promise3()]); console.log(results); console.log(results[0]); console.log(results[1]); console.log(results[2]); // assigns values to individual variables through ''array destructuring'' const [value1, value2, value3] = await Promise.all([promise1(), promise2(), promise3()]); console.log(value1); console.log(value2); console.log(value3); } catch (err) { console.log("there was an error: " + err); } } f1();