Asincronismo
Introducción
El asincronismo es un concepto estrechamente relacionado con JavaScript. En el lado del navegador, las llamadas al servidor (solicitud AJAX), las llamadas a las API web y ciertas funciones de JavaScript (setTimeout, setInterval...) se resuelven de forma asíncrona. En el lado del servidor (con Node.js, por ejemplo), cualquier operación de entrada/salida es asíncrona (como escribir en un archivo, una solicitud HTTP, acceder a una base de datos, etc.). La secuencia de operaciones síncronas y asíncronas puede complicar la legibilidad del código, por lo que, con el tiempo, diversos desarrollos han permitido simplificar la creación y el uso de operaciones asíncronas. A través de los tres apartados que componen este capítulo se estudiarán diferentes técnicas, desde las más antiguas (y, por tanto, menos eficientes) hasta las más recientes (promesas combinadas con async/await).
Callback hell
El uso masivo de callbacks se considera una mala práctica; hablamos, entonces, del infierno de los callbacks. También se la conoce como la Pyramid of doom debido a la indentación que crece con cada llamada a una función asíncrona.
Un callback, también conocido como función de devolución de llamada, es una función a la que se llama en un punto particular del programa. Los motores de JavaScript son de un solo subproceso, lo que significa que solo pueden interpretar una instrucción a la vez. Cuando el motor que ejecuta el código llega a una llamada asíncrona, delega esta llamada a su host (el navegador, Node.js...). Este último suele ser multiproceso, lo que permite procesar varias instrucciones al mismo tiempo. Cuando delega esta llamada, el motor proporciona al host una función conocida como callback. Después de resolver la operación solicitada, el host recupera el resultado y lo pasa como parámetro a la callback que se le proporcionó; luego, esto se coloca en la pila de instrucciones del motor JavaScript. El motor procesa las instrucciones hasta que llega a la devolución de llamada y la ejecuta.
Los callbacks se utilizaron ampliamente en los primeros días de Node.js debido a la falta de una alternativa. Esta técnica tenía como objetivo sincronizar una secuencia de llamadas asíncronas.
Ejemplo:
function employeeExist( ...
Promesa
1. Histórico
La utilización de cadenas de callbacks siempre ha mostrado límites, por lo que ha surgido otra solución: las promesas. El término «promesa» fue acuñado en 1976 para describir una estructura utilizada para sincronizar la ejecución de un programa. En 1988 fue adoptado por Barbara Liskov y Liuba Shrira, quienes inventaron un sistema para encadenar promesas.
En 2010, varias bibliotecas ofrecen soporte para promesas, tales como:
-
JQuery (desde la versión 1.5)
-
Q.js
-
When.js
Estos últimos se basan en una especificación aparecida en 2009 que permite estandarizar la implementación de las promesas: CommonJS Promises/A. Esta especificación evolucionó en 2012 para convertirse en Promise/A+. Las promesas finalmente se estandarizan con ECMAScript 2015, que integra la especificación Promise/A+, de modo que se volvieron utilizables de forma nativa en JavaScript.
En el lado de Node.js, las API se implementaron sin utilizar promesas. Es por eso por lo que la fundación Node.js decidió mantener las API tal como están para no causar incompatibilidad en aplicaciones ya existentes. Sin embargo, Node.js proporciona una función de utilidad, promisify, que permite convertir un callback en una promesa. Sin embargo, esta función se creó para usar con la API de Node.js, por lo que no se garantiza su funcionamiento al convertir una devolución de llamada de una biblioteca de terceros. Como anécdota, Ryan Dahl (el creador de Node.js) explicó en la conferencia JSConf Europe 2018 que tenía la intención de basar Node.js en promesas. Sin embargo, finalmente optó por utilizar callbacks, ya que las encontró más fáciles de usar. Durante varios años, Node.js también ha proporcionado módulos complementarios a las API básicas, que implementan parcialmente su equivalente mediante promesas (ejemplo: el módulo fs/promise).
2. Promesas de ECMAScript 2015
Definida por la especificación ECMAScript 2015, una promesa es un objeto devuelto sincrónicamente por una función asíncrona. Su valor entonces aún no estará determinado y se resolverá en el futuro.
a. Resolución de promesas
La función que realizará una operación asíncrona debe devolver un objeto creado...
Async/await
Las palabras clave async/await aparecen por primera vez en la versión 5 de C# y permiten escribir código que se ejecuta de manera asíncrona, pero con una forma sintácticamente síncrona. Este concepto se ha implementado en TypeScript desde 2015 y se ha estandarizado en la norma ECMAScript 2017.
Las palabras clave async/await no reemplazan las promesas: las complementan. Para declarar operaciones asíncronas, siempre se requieren promesas. Sin embargo, para llamar a las promesas o encadenarlas, se puede utilizar el par async/await a fin de simplificar la escritura del código.
La palabra clave await permite esperar la resolución de una operación asíncrona y recuperar el resultado. Tan pronto como se utiliza la palabra clave await dentro de una función, esta debe tener la palabra clave async presente en su firma. Esta palabra clave indica que la función contiene una operación asíncrona y que el motor JavaScript debe esperar su resolución antes de continuar ejecutando el código de la función.
Sintaxis:
async function fnName() {
await asynchronousOperation();
}
Ejemplo:
const getEmployeeSalary = (employeeId: number) => {
return new Promise<number>((resolve, reject) => {
const xhr = new XMLHttpRequest();
xhr.open("GET", `fakeUrl/${employeeId}`);
xhr.onload = function() {
if (this.status === 200) {
resolve(xhr.response);
} else {
reject({ ...