¿Qué es una promesa?
En JavaScript, una promesa es un objeto que representa el resultado eventual de una operación asíncrona. En palabras simples, una promesa es una acción que puede ser completada (resolve), o negada (reject). Este tipo de objeto se introdujo en la especificación ECMAScript 6 (ES6/ES2015).
Estas promesas tienen 3 estados:
- Pending – La operación asíncrona aún no está completada
- Fulfilled – La operación terminó y la promesa puede tener un resultado.
- Rejected – La operación terminó con un error.
A continuación se muestra como luce una promesa simple:
const goodPromise = new Promise((resolve, reject) => { resolve('promise resolved!'); }); goodPromise.then(result => console.log(result));
En el ejemplo de arriba, primero creamos una variable inmutable llamada goodPromise que contendrá nuestra nueva promesa. Dentro de ella le decimos que envíe como respuesta un String cada que sea resuelta. Más abajo ejecutamos nuestra promesa con el método then(), que nos entrega como resultado el String que ingresamos.
Una promesa no siempre entregará un resultado positivo, por ello también cuentan con un método llamado catch(), el cual tiene como función “cachar” cualquier error o rechazo que suceda durante el proceso de ejecución.
const badPromise = new Promise((resolve, reject) => { reject('promise rejected...'); }); badPromise .then(result => console.log(result)) .catch(error => console.log(error));
¡Bien! Ahora que sabemos que es una promesa y como funciona, veamos algunos de sus otros métodos, que son race(), all(), y finally().
const goodPromise = new Promise((resolve, reject) => { resolve('promise resolved!'); }); const badPromise = new Promise((resolve, reject) => { reject('promise rejected...'); }); const promises = [goodPromise, goodPromise, goodPromise]; Promise.race(promises) .then(result => console.log(result)) .catch(error => console.error(error)) .finally(() => console.log('Promise race is done!')); Promise.all(promises) .then(result => console.log(result)) .catch(error => console.error(error)) .finally(() => console.log('Promise all is done!'));
El método race() nos regresa una promesa que se resuelve o se “cacha” tan pronto como una de las promesas en el iterable nos entregue un resultado o error, all() nos regresa una promesa que se resuelve cuando todas las promesas en el iterable pasaron, en caso de haber un error se “cacha” la promesa que entrega este mismo.
El método finally() es nuevo, viene bajo la especificación más reciente (ES2018), en este podemos ejecutar algún pedazo de código en una función sin argumento en caso de que la promesa haya sido fulfilled o rejected.
También existe async/await. No hay que asustarnos por este tipo de declaración, son solo funciones que trabajan de manera asíncrona a través del event loop, que nos regresan promesas como respuesta.
Yo, en lo personal, prefiero trabajar con async/await, porqué:
- La sintaxis y estructura usando este tipo de declaración hace que tu código sea más legible.
- Podemos manejar errores usando el clásico try/catch.
- El “debugging” es mucho más fácil.
const request = require('request'); const uri = 'https://randomuser.me/api/?results=5'; const promiseGet = (uri = '') => new Promise((resolve, reject) => { request.get(uri, (error, response) => { if (error) { return reject(error); } resolve(response); }); }); const asyncFunc = async () => { try { const pendingPromise = promiseGet(uri); console.log(pendingPromise); const { body } = await pendingPromise; console.log(JSON.parse(body)); } catch (error) { console.log(error); } }; asyncFunc().finally(() => console.log('async/await is done!'));
En este ejemplo instalé el paquete request, después lo envolví en una promesa. Después declaré una función asíncrona que ejecuta la promesa que creé anteriormente, esta promesa aun no está resuelta o rechazada así que tenemos que esperar (await). Cuando esta entrega una respuesta, la destructuro, y por último imprimo el objeto que me entrega.
Conclusión
Manejar promesas nos ayuda a manejar múltiples procesos sin necesidad de bloquear un proceso principal, y con la declaración async/await nuestro código se vuelve un poco más legible, y usando algunas otras funciones del ES6 como la destructuración, y métodos como all() lo hacen aún más poderoso.