javascript - termine - node js async await ejemplos
Llame a funciones asÃncronas/en espera en paralelo (9)
La solución intuitiva
function wait(ms, data) {
console.log(''Starting task:'', data, ms);
return new Promise( resolve => setTimeout(resolve, ms, data) );
}
(async function parallel() {
// step 1 - initiate all promises
console.log(''STARTING'')
let task1 = wait(2000, ''parallelTask1'') // PS: see Exception handling below
let task2 = wait(500, ''parallelTask2'')
let task3 = wait(1000, ''parallelTask3'')
// step 2 - await all promises
console.log(''WAITING'')
task1 = await task1
task2 = await task2
task3 = await task3
// step 3 - all results are 100% ready
console.log(''FINISHED'')
console.log(''Result:'', task1, task2, task3)
})()
- Esto ejecutará las promesas una por una, pero al instante y continuarán ejecutándose simultáneamente.
- Aquí es donde hacemos una pausa en la ejecución de código adicional y esperamos a que finalicen. No importa el orden y cuál se resuelve primero. El código no continuará con el paso 3 antes de que todos se resuelvan. Si el primero lleva más tiempo, no tendrá que esperar más el segundo, ya que se habría cumplido cuando llegue el código.
-
Está hecho, la última de las promesas se resolvió y la ejecución del código se completó la última llamada en
await
.
Con ES6, incluso puede hacer esto en el paso 2, después de la iniciación ejecutada
[task1, task2, task3] = [await task1, await task2, await task3]
PD: también puedes esperar dentro de los cálculos
let five = getAsyncFive()
let ten = getAsyncTen()
let result = await five * await ten
* tenga en cuenta que no es lo mismo que
let result = await getAsyncFive() * await getAsyncTen()
ya que esto no ejecutará las tareas asincrónicas en paralelo.
manejo de excepciones
En el fragmento a continuación, el
.catch(e => e)
detecta un error y permite que la cadena continúe, permitiendo que la promesa se
resuelva
, en lugar de
rechazar
.
Sin la
catch
, el código arrojaría una excepción no controlada y la función saldría temprano.
const wait = (ms, data) => log(ms,data) || new Promise( resolve => setTimeout(resolve, ms, data) )
const reject = (ms, data) => log(ms,data) || new Promise( (r, reject) => setTimeout(reject, ms, data) )
const e = e => ''err-'' + e
const l = l => (console.log(''Done:'', l), l)
const log = (ms, data) => console.log(''Started'', data, ms)
;(async function parallel() {
let task1 = reject(500, ''parallelTask1'').catch(e).then(l)
let task2 = wait(2000, ''parallelTask2'').catch(e).then(l)
let task3 = reject(1000, ''parallelTask3'').catch(e).then(l)
console.log(''WAITING'')
task1 = await task1
task2 = await task2
task3 = await task3
console.log(''FINISHED'', task1, task2, task3)
})()
El segundo fragmento no se maneja y la función fallará.
También puede abrir Devtools y ver los errores en la salida de la consola.
const wait = (ms, data) => log(ms,data) || new Promise( resolve => setTimeout(resolve, ms, data) )
const reject = (ms, data) => log(ms,data) || new Promise( (r, reject) => setTimeout(reject, ms, data) )
const e = e => ''err-'' + e
const l = l => (console.log(''Done:'', l), l)
const log = (ms, data) => console.log(''Started'', data, ms)
console.log(''here1'')
;(async function parallel() {
let task1 = reject(500, ''parallelTask1'').then(l) // catch is removed
let task2 = wait(2000, ''parallelTask2'').then(l)
let task3 = reject(1000, ''parallelTask3'').then(l)
console.log(''WAITING'')
task1 = await task1
task2 = await task2
task3 = await task3
console.log(''FINISHED'', task1, task2, task3)
})()
console.log(''here2'') // Note: "FINISHED" will not run
Por lo que yo entiendo, en ES7 / ES2016, poner múltiples en
await
en el código funcionará de manera similar a encadenar
.then()
con promesas, lo que significa que se ejecutarán uno tras otro en lugar de en paralelo.
Entonces, por ejemplo, tenemos este código:
await someCall();
await anotherCall();
¿Entiendo correctamente que se
anotherCall()
solo cuando se
someCall()
?
¿Cuál es la forma más elegante de llamarlos en paralelo?
Quiero usarlo en Node, ¿tal vez hay una solución con la biblioteca asíncrona?
EDITAR: No estoy satisfecho con la solución proporcionada en esta pregunta: Desaceleración debido a la espera no paralela de promesas en generadores asíncronos , porque usa generadores y estoy preguntando sobre un caso de uso más general.
Creo una función auxiliar waitAll, puede ser que la haga más dulce. Solo funciona en nodejs por ahora, no en el navegador Chrome.
//const parallel = async (...items) => {
const waitAll = async (...items) => {
//this function does start execution the functions
//the execution has been started before running this code here
//instead it collects of the result of execution of the functions
const temp = [];
for (const item of items) {
//this is not
//temp.push(await item())
//it does wait for the result in series (not in parallel), but
//it doesn''t affect the parallel execution of those functions
//because they haven started earlier
temp.push(await item);
}
return temp;
};
//the async functions are executed in parallel before passed
//in the waitAll function
//const finalResult = await waitAll(someResult(), anotherResult());
//const finalResult = await parallel(someResult(), anotherResult());
//or
const [result1, result2] = await waitAll(someResult(), anotherResult());
//const [result1, result2] = await parallel(someResult(), anotherResult());
Hay otra forma sin Promise.all () para hacerlo en paralelo:
Primero, tenemos 2 funciones para imprimir números:
function printNumber1() {
return new Promise((resolve,reject) => {
setTimeout(() => {
console.log("Number1 is done");
resolve(10);
},1000);
});
}
function printNumber2() {
return new Promise((resolve,reject) => {
setTimeout(() => {
console.log("Number2 is done");
resolve(20);
},500);
});
}
Esto es secuencial:
async function oneByOne() {
const number1 = await printNumber1();
const number2 = await printNumber2();
}
//Output: Number1 is done, Number2 is done
Esto es paralelo:
async function inParallel() {
const promise1 = printNumber1();
const promise2 = printNumber2();
const number1 = await promise1;
const number2 = await promise2;
}
//Output: Number2 is done, Number1 is done
He creado una esencia que prueba algunas formas diferentes de resolver promesas, con resultados. Puede ser útil ver las opciones que funcionan.
Puedes esperar en
Promise.all()
:
await Promise.all([someCall(), anotherCall()]);
Para almacenar los resultados:
let [someResult, anotherResult] = await Promise.all([someCall(), anotherCall()]);
Yo voto por:
await Promise.all([someCall(), anotherCall()]);
Tenga en cuenta el momento en que llama a las funciones, puede causar resultados inesperados:
// Supposing anotherCall() will trigger a request to create a new User
if (callFirst) {
await someCall();
} else {
await Promise.all([someCall(), anotherCall()]); // --> create new User here
}
Pero el siguiente siempre activa la solicitud para crear un nuevo usuario
// Supposing anotherCall() will trigger a request to create a new User
const someResult = someCall();
const anotherResult = anotherCall(); // ->> This always creates new User
if (callFirst) {
await someCall();
} else {
const finalResult = [await someResult, await anotherResult]
}
Actualizar:
La respuesta original hace que sea difícil (y en algunos casos imposible) manejar correctamente los rechazos de promesas.
La solución correcta es usar
Promise.all
:
const [someResult, anotherResult] = await Promise.all([someCall(), anotherCall()]);
Respuesta original:
Solo asegúrese de llamar a ambas funciones antes de esperar una:
// Call both functions
const somePromise = someCall();
const anotherPromise = anotherCall();
// Await both promises
const someResult = await somePromise;
const anotherResult = await anotherPromise;
TL; DR
Use
Promise.all
para las llamadas a funciones paralelas, los comportamientos de respuesta no son correctos cuando se produce el error.
Primero, ejecute
todas
las llamadas asincrónicas a la vez y obtenga todos los objetos
Promise
.
En segundo lugar, use
await
en los objetos
Promise
.
De esta manera, mientras espera a que la primera
Promise
resuelva, las otras llamadas asincrónicas siguen progresando.
En general, solo esperará tanto como la llamada asincrónica más lenta.
Por ejemplo:
// Begin first call and store promise without waiting
const someResult = someCall();
// Begin second call and store promise without waiting
const anotherResult = anotherCall();
// Now we await for both results, whose async processes have already been started
const finalResult = [await someResult, await anotherResult];
// At this point all calls have been resolved
// Now when accessing someResult| anotherResult,
// you will have a value instead of a promise
Ejemplo de JSbin: http://jsbin.com/xerifanima/edit?js,console
Advertencia:
no importa si las llamadas en
await
están en la misma línea o en líneas diferentes, siempre que la primera llamada en
await
ocurra
después de
todas las llamadas asincrónicas.
Ver el comentario de JohnnyHK.
Actualización:
esta respuesta tiene un tiempo diferente en el manejo de errores de acuerdo con la
respuesta de @ bergi
,
NO
arroja el error cuando ocurre el error, pero después de que se ejecutan todas las promesas.
[result1, result2] = Promise.all([async1(), async2()])
el resultado con el consejo de @ jonny:
[result1, result2] = Promise.all([async1(), async2()])
, verifique el siguiente fragmento de código
const correctAsync500ms = () => {
return new Promise(resolve => {
setTimeout(resolve, 500, ''correct500msResult'');
});
};
const correctAsync100ms = () => {
return new Promise(resolve => {
setTimeout(resolve, 100, ''correct100msResult'');
});
};
const rejectAsync100ms = () => {
return new Promise((resolve, reject) => {
setTimeout(reject, 100, ''reject100msError'');
});
};
const asyncInArray = async (fun1, fun2) => {
const label = ''test async functions in array'';
try {
console.time(label);
const p1 = fun1();
const p2 = fun2();
const result = [await p1, await p2];
console.timeEnd(label);
} catch (e) {
console.error(''error is'', e);
console.timeEnd(label);
}
};
const asyncInPromiseAll = async (fun1, fun2) => {
const label = ''test async functions with Promise.all'';
try {
console.time(label);
let [value1, value2] = await Promise.all([fun1(), fun2()]);
console.timeEnd(label);
} catch (e) {
console.error(''error is'', e);
console.timeEnd(label);
}
};
(async () => {
console.group(''async functions without error'');
console.log(''async functions without error: start'')
await asyncInArray(correctAsync500ms, correctAsync100ms);
await asyncInPromiseAll(correctAsync500ms, correctAsync100ms);
console.groupEnd();
console.group(''async functions with error'');
console.log(''async functions with error: start'')
await asyncInArray(correctAsync500ms, rejectAsync100ms);
await asyncInPromiseAll(correctAsync500ms, rejectAsync100ms);
console.groupEnd();
})();
// A generic test function that can be configured
// with an arbitrary delay and to either resolve or reject
const test = (delay, resolveSuccessfully) => new Promise((resolve, reject) => setTimeout(() => {
console.log(`Done ${ delay }`);
resolveSuccessfully ? resolve(`Resolved ${ delay }`) : reject(`Reject ${ delay }`)
}, delay));
// Our async handler function
const handler = async () => {
// Promise 1 runs first, but resolves last
const p1 = test(10000, true);
// Promise 2 run second, and also resolves
const p2 = test(5000, true);
// Promise 3 runs last, but completes first (with a rejection)
// Note the catch to trap the error immediately
const p3 = test(1000, false).catch(e => console.log(e));
// Await all in parallel
const r = await Promise.all([p1, p2, p3]);
// Display the results
console.log(r);
};
// Run the handler
handler();
/*
Done 1000
Reject 1000
Done 5000
Done 10000
*/
Si bien la configuración de p1, p2 y p3 no los ejecuta estrictamente en paralelo, no retrasan ninguna ejecución y puede atrapar errores contextuales con una captura.