...
En JavaScript podemos encontrarnos con código asíncrono, con el tipo de dato Promesa que, por ejemplo, obtenemos como respuesta de las llamadas a servicios REST con axios. Para tratar este código, tenemos dos formas principales: con los métodos .then(), .catch() y .finally() que proporcionan las promesas, o con async y await.
Tipo de dato Promise
En JavaScript, el tipo Promise es un objeto que se usa para funcionalidades asíncronas, y representa un valor que se resolverá en el futuro, ya sea con éxito o con error.
Crear objetos Promise
Aunque por lo general no necesitaremos crear nuestros propios Promise, es importante conocer cómo se haría. La sintaxis del constructor del objeto promise sería la siguiente:
| Bloque de código | ||
|---|---|---|
| ||
let promise = new Promise(function(resolve, reject) {
// executor (el código que ejecutará la Promise)
}); |
La función que se le pasa al new Promise se llama executor, que se ejecuta automáticamente cuando se crea la Promise, y producirá el resultado. Esta función devuelve un resultado llamando a uno de estos dos callbacks:
resolve(value)- si la tarea ha finalizado con éxito, devolviendovalue.reject(error)- si se ha producido un error, devolviendo el objetoerror.
Cómo consumir objetos Promise
Utilizando .then(), .catch() y .finally()
Estos tres métodos tienen un funcionamiento que se asemeja al try - catch - finally que podemos conocer de Java. Se ejecuta el código que se indique en .then( ... ) si la promesa se resuelve correctamente, se ejecuta .catch( ... ) si da un error, y tras ejecutarse uno u otro, se ejecuta siempre .finally( ... ).
.then( ... )
El método principal para el tratamiento de las Promise será .then, pues nos permite tratar una promesa exitosa, pero también nos permite definir el tratamiento de errores, pudiendo ahorrarnos el .catch. El código a ejecutar en cada caso se definiría con dos funciones, de esta forma:
...
El primer argumento define la función que se ejecutará en el momento en el que se resuelva la promesa, y el segundo argumento la función que se ejecutará si se devuelve un error. Si solo estamos interesados en reaccionar a una respuesta exitosa, podemos indicar únicamente el primer argumento.
.catch( ... )
Con .catch podemos definir el tratamiento de problemas que se resuelven en error. Como hemos visto, este tratamiento se puede incluir como segundo argumento de .then, pero si queremos utilizarlo para que el código sea más legible, o porque solo queremos ejecutar código en caso de error, se haría así:
| Bloque de código | ||
|---|---|---|
| ||
promise.catch((error) => { /* Código en respuesta a un error */ }); |
finally( ... )
Por último, la función que indiquemos en el .finally se ejecutará después de que la promesa haya dado un valor, ya sea correcto o error, y su sintaxis sería como en los casos anteriores:
...
Una particularidad del finally es que realmente no tiene por qué ser el último código que se ejecute. Then catch y finally se ejecutan una vez se ha resuelto la promesa, pero si ponemos el finally antes de los otros métodos, su código se ejecutará antes, independientemente de cómo se haya resuelto la promesa.
Utilizando async/await
Posteriormente a las promesas, se incluyeron async y await, con el que podemos hacer el tratamiento del código asíncrono con un código más limpio, pues si encadenamos varias promesas, los bloques vistos en el apartado anterior anidados pueden ser poco legibles.
Async
Async es un modificador que podemos añadir a las funciones para especificar que estas sean asíncronas:
| Bloque de código | ||
|---|---|---|
| ||
async function asyncCall() {
...
} |
De esta forma, lo que se hace es que esa función devuelva un Promise, aunque no la creemos explícitamente. Si una función no incluye el async, pero devuelve un Promise, se considera async igualmente.
Await
Cuando hacemos una llamada a una función asíncrona, es decir, que devuelva una Pormise, podemos incluir el await delante de la llamada:
| Bloque de código | ||
|---|---|---|
| ||
const response = await fetch(…); |
Lo que hace await es indicar que se espere a que la promesa se resuelva, y que se devuelva el resultado. Es decir, await bloquea la ejecución dentro de la función en la que nos encontramos hasta que se resuelva la promesa. Cuando usamos await, todo lo que haya debajo dentro de la función es como si se ejecutase en .then. Por ejemplo, teniendo este código:
| Bloque de código | ||
|---|---|---|
| ||
const response = await fetch(…);
const json = await response.json();
const foo = JSON.parse(json); // Using json here, even though my request was async! |
Este otro código sería equivalente:
| Bloque de código | ||
|---|---|---|
| ||
fetch(…)
.then(response => response.json())
.then(json => {
const foo = JSON.parse(json);
}); |
Pero hay que entender e insistir en que await encapsula el resto de la función en la que nos encontramos. Por explicarlo con un ejemplo, teniendo el siguiente código:
| Bloque de código | ||
|---|---|---|
| ||
async function asynchronousCall() { ... };
function callWithAwait() {
console.log("Entramos en callWithAwait");
var response = await asynchronousCall();
console.log("Promesa resuelta");
}
function funcionPrincipal() {
console.log("Inicio de función principal");
callWithAwait();
console.log("Fin de función principal")
} |
Suponiendo que asynchronousCall tarda unos segundos en resolverse, es decir, es lo último que termina, el último console.log de la función principal se ejecutaría antes. El orden en el que veríamos los logs sería este:
Inicio de función principal
Entramos en callWithAwait
Fin de función principal
Promesa resuelta
Si nos aclaramos más con .then, para aclararnos, este sería el código equivalente:
| Bloque de código | ||
|---|---|---|
| ||
async function asynchronousCall() { ... };
funcionPrincipal() {
console.log("Inicio de función principal");
console.log("Entramos en callWithAwait");
asynchronousCall().then(() => {
console.log("Promesa resuelta");
});
console.log("Fin de función principal")
} |
Teniendo claro el funcionamiento de await, y organizando nuestro código de acuerdo con éste, podemos usar indistintamente await o .then.
