Hola, hoy vamos a ver uno de los pilares fundamentales de Javascript, la sincronía. Javascript utiliza un modelo asíncrono y no bloqueante. Para ello utiliza un bucle de eventos en el que corre un único hilo. De esta forma consigue ser altamente concurrente a pesar de emplear ese único hilo. Las apreciaciones asíncronas las podemos clasificar en dos grandes grupos. Por un lado tenemos los eventos o funciones del API de navegador web que manejamos mediante callback y por otro lado tenemos las operaciones de entrada-salida como pueden ser lectura y escritura a disco, llamada API de terceros o acceso a base de datos que vamos a manejar mediante promesas. Vamos a ver todos ellos. El primero que vamos a ver es callback. Callback es una devolución de llamada. ¿Y qué es un callback? Pues no es nada más que una función que se pasa como argumento a otra función y que será invocada para completar algún tipo de acción en el futuro. El callback es la pieza clave que Javascript utiliza para poder trabajar de forma asíncrona. De hecho entendemos que el resto de patrones que veremos a continuación están basados de una manera u otra en callback. En el siguiente ejemplo tenemos la función setTimeout que pasamos como argumentos, como primer argumento pasamos una función a la que le denominamos callback que se ejecutará después de un tiempo, en este caso después de un segundo. En el siguiente ejemplo hemos definido la función ejemploCallback con dos parámetros, país y callback. Esos dos parámetros, uno es de tipo texto, string, y otro es de tipo variable. Y a estos dos parámetros cuando llamamos a la función les pasamos por un lado el texto Australia y por otro lado la función regreso. Esta función regreso al llamarse al finalización de la función ejemploCallback la llamaremos y la denominaremos callback. Vamos a ver esto ejecutando el código. Aquí tenemos la función escrita anteriormente, la vamos a ejecutar. Y vemos que esta función se ejecuta de forma síncrona. Se llama a la función, le pasa los parámetros y lo primero que hace es mostrarnos por pantalla, nos vamos a Australia y más allá. A continuación ejecuta la función callback, ya estamos de vuelta, y a continuación el fin del programa. Fin del programa. Hemos visto... Que esta función se ejecuta de forma lineal, de forma síncrona. Y esto nos hace plantearnos lo siguiente. Pero ¿cómo? Si estamos utilizando callback y estamos hablando de asincronía, esta función es síncrona. Pues sí, porque debemos entender que el mero hecho de utilizar callback no significa dotar de asincronía a nuestro código. Bien, vamos a ver esto en el visualizador. Bien. Vamos a ir observando como carga las funciones en la pila de llamadas. Y las va ejecutando. Vale. Todas las funciones han pasado por la pila de llamada, con lo cual se ejecutan de forma secuencial. ¿Cómo podemos convertir este código en código lineal? ¿Con código asíncrono? Pues utilizando una función asíncrona. Cambiamos nuestra función regreso por una función asíncrona. En este caso, utilizamos un setTimeout y veremos el cambio. Se carga la pila de llamadas. Recordar, esta pila de llamadas es una lista LIFO, que es el último en entrar, es el primero en salir. Una vez hecha toda la carga, veis que el setTimeout de regreso lo ha cargado en la cola de tareas. ¿Qué ejecutará primero? Pues lo vamos a ver. Hasta que no se vacíe la pila de llamadas, no hará llamadas a las otras colas. Ahora hemos vaciado la pila de llamadas. Se ejecutará otra función que teníamos aquí al final que se llama saludar. Y, como vemos, nos deja para el final la función que tenemos en la cola de tareas. Aquí sí tenemos asíncronía. Si nos hemos fijado, nos vamos a Australia, fin del programa, pasa un tiempo y ejecutamos y ya estamos de vuelta. Bueno. Uno de los problemas que se presentan al programar con callback es lo que denominamos el callback hell, el callback del infierno. ¿Y qué nos presenta esto? Pues nos presenta una excesiva identación, resta la elegibilidad al código, dificulta su mantenimiento y añade una complejidad ciclomática. Si queremos aplicar buenas prácticas y tener un código mantenible, pues este es uno de los puntos que debiéramos abordar. Y para ello vienen en nuestra ayuda las soluciones presentadas por JavaScript en las siguientes versiones. Atendiendo al estándar emacscript 2015, o denominado también ES6, JavaScript introduce un elemento nuevo, las promesas. Una promesa es un objeto que representa la eventual finalización o falla de una llamada asíncrona. Falla o ha salido bien. O sea, respuestas que no se producen de inmediato. Esto nos recuerda a los callbacks. Podemos pensar, por ejemplo, en todas aquellas operaciones de entrada-salida, como decíamos antes, acceso a disco, lectura-escritura, acceso a bases de datos, que no sabemos, bien sea por el estado de la red, cuánto van a tardar en resolverse. Para crear una promesa utilizamos el constructor promise, al cual le pasamos dos parámetros. En este caso, esos dos parámetros son dos funciones de devoluciones de llamada. El primer parámetro, resolve, en él podemos ejecutar una función asíncrona dentro de la devolución de llamada y luego resolverla si esto tiene éxito. Y, en cambio, si rechazo, si hay un problema, ejecutaremos la función de llamada reject, mejor dicho, la función de devolución de llamada. Y para recibir estos estados, tanto el de estado cumplida como el de rechazada, tenemos dos métodos, el método dem para recibir la promesa cumplida y el método catch para recibir la promesa rechazada. Vamos a ver un ejemplo. Vale. Aquí tenemos definido una promesa con dos parámetros y la cual, bueno, la hemos trampeado un poquito, aquí con un if, definiéndole si la promesa se va a resolver o se va a rechazar. En este caso vamos a ver qué ocurre si la promesa se acepta. Vamos a ver. Vale. Ejecutamos la promesa, en este caso, y al ejecutarse la promesa nos pregunta por el flag, está true y nos hace un resolve. Vamos a recibir el resultado, entra por el den y nos da punto den promesa resuelta correctamente. Vamos al false. La volvemos a ejecutar y vemos que si la promesa no se resuelve, la recogemos después con el catch. Muy bien. Vale. En el estándar de 2017, Javascript da un paso más y implementa los métodos async await, las palabras claves async await que vienen a solucionar algunos de los problemas que nos pueden dar las promesas en cuanto, por ejemplo, a la legibilidad del código. La firma de una función asincrona está marcada por la palabra clave async antes de la palabra clave function. Bueno, aquí vemos que esto es un array function e indica que la promesa será automáticamente devuelta. Podemos declarar como async tanto funciones con nombre, como funciones anónimas, como en este caso funciones flecha. El await debe ser usado siempre dentro de una función declarada como async. Y espera automáticamente de una forma asíncrona y no bloqueante que la promesa sea resuelta. Con las funciones asíncronas lo que conseguimos es escribir un programa asíncrono de forma que sea asíncrono, ¿vale? Vamos a ver el proceso ya que ahora conocemos las promesas y conocemos los callbacks y conocemos las funciones convencionales cómo utiliza JavaScript todo esto y cómo lo distribuye. Tenemos tres elementos importantes de almacenamiento. Por un lado está la pila de llamadas que será una lista LIFO Acordaros, el primero en entrar es el último en salir y dos colas que es el primero en entrar es el primero en salir. La pila de llamadas será la que ejecute todas las funciones de nuestro código En la cola de tareas nos vendrán los callbacks simples y en la cola de micro tareas serán las promesas, que como hemos dicho están basadas en callbacks pero es un artefacto utilizado por JavaScript a partir del ES6. No aparece async await porque async await no es un artefacto nuevo sino que simplemente es una forma de reescribir las promesas. Lo denominamos como azúcar sintáctico. Y todo esto viene manejado por el event loop. El event loop va comprobando que la pila de llamadas esté vacía y entonces consulta cada una de las colas para inyectar código a la pila de llamadas ¿En qué orden lo hará? Pues primero vigilará la cola de micro tareas y cuando ésta esté vacía pues llamará a la cola de tareas Vamos a ver un ejemplo en el visualizador Aquí tenemos un programilla que lo primero que tenemos es un callback, una promesa y una función autoinvocada Vemos que en la cola de tareas nos carga el callback, en la cola de micro tareas nos carga la promesa y en el callstart nos carga la función autoinvocada. Una vez que tenemos la carga hecha, se ejecuta el programa y veis que lo primero que se ejecuta es lo que tengas en la pila de llamadas. Y en este caso como es la función saludar pues dice fin del programa. A continuación ejecuta lo que tenemos en la cola de micro tareas que es la promesa. Cuando el callstart está vacío y la cola de micro tareas también está vacía consulta la cola de tareas y ejecuta finalmente la cola de tareas Es importante que comprobéis vosotros este concepto y probéis con varios programas pequeñitos, rutinas para ver cuando se ejecuta uno u otro ¿vale? Como hemos dicho, el proceso de ejecución es el siguiente. Si tengo algo en la cola de micro tareas lo ejecutará hasta que se vacíe. Por contra si aparece algo en la cola de tareas sólo ejecutaré una de las funciones que tenga en la cola de tareas. Esto quiere decir que una vez ejecutada esta función volverá otra vez a consultar la cola de micro tareas. Bueno, aquí tenemos un ejemplo, o tenemos un gráfico de cómo funciona todo el sistema Las llamadas, bien sean llamadas de navegador o bien sean de los métodos propios de Javascript pasan, en este caso estas son callbacks pasan a la cola de tareas y aquellas que sean procesos, en este caso estamos mezclando ya Node a la cola de micro tareas. El bucle de eventos las revisará y las pasará a la pila de llamadas Yo os invito a ir practicando este tema y nos vemos en el siguiente tema ¡Gracias!