4Geeks logo
4Geeks logo
Sobre Nosotros

Catálogo de contenidos

Para los geeks autodidactas, este es nuestro extenso catálogo de contenido con todos los materiales y tutoriales que hemos desarrollado hasta el día de hoy.

Tiene sentido comenzar a aprender leyendo y viendo videos sobre los fundamentos y cómo funcionan las cosas.

Full-Stack Software Developer

Data Science and Machine Learning - 16 wks

Buscar en lecciones

Aprendizaje social y en vivo

La forma más eficiente de aprender: Únete a una cohorte con compañeros, transmisiones en vivo, sesiones improvisadas de codificación, tutorías en vivo con expertos reales y mantenga la motivación.

De cero a que te paguen como desarrollador, aprende las habilidades del presente y del futuro. Impulsa tu carrera profesional y sé contratado por una empresa tecnológica.

Empezar a programar

← Regresar a lecciones
Editar en Github

Creando algoritmos asíncronos

Programación Asíncrona con JavaScript
  • Asincrónico

Programación Asíncrona con JavaScript

Hasta ahora, hemos utilizado código JavaScript para ejecutar aplicaciones web simples, que incluyen: usar variables, llamar a funciones y jugar con el DOM. En las funciones, específicamente, incluso pasamos funciones a otras funciones (funciones callback) y hay mucho más que hablar al respecto.

Comencemos diciendo que JavaScript por defecto es síncrono y con una sola secuencia, es decir: el código se ejecuta desde la línea 1 hasta el último, uno a la vez y en ese orden normalmente. Échale un vistazo a este ejemplo:

Sincróno(por defecto)

1function ejecutarPrimero(){ 2 console.log("primero"); 3} 4function ejecutarSegundo(){ 5 console.log("segundo"); 6} 7ejecutarSegundo(); 8ejecutarPrimero(); 9 10/* 11RESULTADO EN CONSOLA: 12 > segundo 13 > primero 14*/

Aquí: la línea 5 se ejecuta antes de la línea 2 porque estamos llamando a correSegundo () (línea 7) antes de correPrimero () (línea 8). Rompiendo el orden de mando de la computadora que llama (o ejecuta) el bloque de código dentro de una función.

Las cosas se complican más cuando se llaman funciones dentro de funciones, como podemos ver aquí:

Funciones de Llamada

1function correPrimero(){ 2 console.log("Quiero correr primero"); 3 correSegundo(); 4 console.log("Yo también quiero correr cuando correPrimero corra"); 5} 6function correSegundo(){ 7 console.log("¿Dónde estoy corriendo?"); 8} 9correPrimero(); 10 11/* 12RESULTADO CONSOLA: 13 > Quiero correr primero 14 > ¿Donde estoy corriendo? 15 > También quiero correr cuando se ejecuta correPrimero //Esta línea de código tuvo que esperar a que correSegundo() terminara. 16*/

OK qué...?

Esto sucede porque la call stack en JavaScript lleva un registro de las funciones que se están ejecutando actualmente y se están procesando:

  • correPrimero() se añade al call stack porque la llamamos (línea 9).
  • Vemos nuestro primer console.log (línea 2), después de eso, se llama a correSegundo () (línea 3).
  • correPrimero () hace una pausa en su ejecución y correSegundo () comienza a ejecutarse.
  • Segundo console.log se ejecuta (linea 7).
  • Una vez que correSegundo () termina, correPrimero () comienza nuevamente, ejecutando el resto de su código, el último console.log (línea 4).

¡ D I V I E R T E T E !

Pero espera, hay más... Incluso podríamos pasar una función como argumento a otra función (no, esto no es un error tipográfico). La función enviada como parámetro se llama función callback. Echa un vistazo:

Funciones de Devolución de Llamada

11 function correPrimero(unaFuncion){ 22 console.log("Quiero correr primero"); 33 correPrimero(); 44 correSegundo(); 55 console.log("También quiero correr cuando se ejecute correPrimero"); 66 } 77 function correSegundo(){ 88 console.log("¿Donde estoy corriendo?"); 99 } 1010 correPrimero(unaTercera); 1111 1212 function unaTercera(){ 1313 console.log("Esto es una locura"); 1414 } 1515 16 17/* 18RESULTADO CONSOLA: 19 > Quiero correr primero 20 > Esto es una locura 21 > ¿Donde estoy corriendo? 22 > También quiero correr cuando se ejecuta correPrimero 23*/

...Es posible que quieras repasar, no te preocupes, tenemos tiempo...

¡Tiempo de explicaciones!

Hemos agregado una nueva función unaTercera () (línea 12), que muestra los registros de la consola: "esto es una locura"; pero no la estamos llamando directamente, en cambio, estamos pasando el nombre como parámetro a correPrimero () (línea 10). correPrimero(unaFunción) ahora espera un valor (línea 1) que llamaremos como si fuera una función (línea 3). Ten en cuenta que el nombre es diferente porque pasamos el valor, no el nombre de la variable. Esto produce una nueva impresión en la consola: "esto es una locura", antes de llamar a correSegundo () (línea 4).

...jump around!, jump around!, jump around!, Jump up, jump up and get down! (music)...

Ahora, supongamos que necesitamos cargar algunos archivos desde un servidor, específicamente, imágenes:

Carga síncrona de imágenes.

1function cargarImagen(){ 2 console.log("¡Cárgala!"); 3 //código para cargar una imagen 4 console.log("¡Imagen cargada!"); 5} 6function usuarioEsperando(){ 7 console.log("No me gusta esperar"); 8} 9cargarImagen (); 10usuarioEsperando (); 11 12/*RESULTADO CONSOLA 13 > ¡Cárgala! //el usuario comienza a esperar 14 //ahora el usuario tiene que esperar a que lleguen las imágenes, hora: desconocido... navegador: congelado :( 15 > ¡Imagen cargada! //después ?? segundos 16 > No me gusta esperar //No queremos que los usuarios esperen tanto tiempo para ver las imágenes. 17*/

Inaceptable...

En un sitio web de la vida real, los usuarios tendrán que esperar mucho tiempo para ver algo, todo porque el procesamiento DOM tiene que esperar a que lleguen las imágenes desde el servidor; y esto es todo porque estamos usando el mismo hilo de ejecución para todo.

Asincrónico

La programación asíncrona es una forma de procesar líneas de código y manejar el resultado sin afectar el hilo principal.

1function cargarImagen(){ 2 console.log("¡Cárgala!"); 3 fetch("la_url_de_la_imagen").then( (response) => { 4 if(response.ok){ 5 console.log("¡Imagen cargada!"); 6 } else { 7 console.log("Uh-oh algo salió mal"); 8 } 9 }); 10} 11function usuarioEsperando(){ 12 console.log("No me gusta esperar"); 13} 14cargarImagen(); 15usuarioEsperando(); 16 17 18/*RESULTADO CONSOLA: 19 > ¡Cárgala! //el usuario comienza a esperar 20 > No me gusta esperar //¡sin espera! DOM listo para ver 21 //... y ?? segundos más tarde 22 > ¡Cárgala! OR Uh-oh algo salió mal //¡Imagen!... Mágico! || Oops, no hay imágenes 23*/

Javascript ofrece varias funciones asíncronas predefinidas que podemos utilizar para resolver cualquier escenario posible. Algunas de ellas son:

  • Fetch API: se utiliza para cargar archivos de forma asíncrona.
  • setTimeout(): se utiliza para establecer temporizadores entre bloques de código.

En este caso, utilizamos la Fetch API para cargar las imágenes y luego (después de obtener una respuesta desde backend) escribimos algunos comentarios sobre el proceso.

Ten en cuenta que cualquier peticion (request) http puede fallar por diversas razones, siempre debemos estar preparados para la falla.

Promesas

Una promesa no es más que el resultado de una operación asíncrona. Representa la finalización o el fracaso de ese resultado en un objeto proporcionado por la promesa.

Una promesa (promise) tiene 3 estados diferentes:

  • Pendiente: el resultado de la promesa aún no se ha determinado porque la operación asíncrona no se ha completado.
  • Cumplida: es cuando la operación asíncrona finaliza y la promesa devuelve un valor como un objeto.
  • Rechazada: tiene lugar cuando la operación falló.

Así es como se puede crear una promesa.

1var myPomise = new Promise(function(resolve, reject) { 2 setTimeout(function() { 3 resolve("Yo estaba resuelto"); 4 }, 300); 5}); 6myPomise.then((obj) => { 7 console.log(obj); 8}); 9console.log(myPomise); 10 11/*RESULTADO EN CONSOLA: 12 >Objeto de promesa // devolverá un objeto de promesa 13 >"Estaba resuelto" 14*/

Funciones Resolve y Reject

  • Resolve se utiliza para cambiar el estado de una promesa de pendiente a cumplida.
  • Reject se utiliza para cambiar el estado de pendiente a rechazado.

Métodos importantes que debemos conocer al usar promesas.

  • resolve: devuelve un objeto de promesa que tiene el status de resuelto con un valor.
1 //aquí Promesa representa el objeto Promesa. 2 Promise.resolve("Yo estaba resuelto con este valor").then(value => console.log(value)); 3 4 /*RESULTADO EN CONSOLA: 5 >"Yo estaba resuelto con este valor" 6 7 *********** 8 Un mejor enfoque sería inicializar una variable. 9 igual a la promesa resuelta. 10 11 --- ejemeplo: 12 var myResolvedPromise = Promise.resolve("Yo estaba resuelto con este valor"); 13 */
  • reject: devuelve una promesa rechazada por un motivo.
1 Promise.reject(new Error("fui rechazado")).then(error => console.log(error));
  • then: este método devuelve una promesa y puede tomar hasta 2 argumentos. Una para la promesa resuelta y otra para la promesa rechazada. Arriba hay un ejemplo que usa el método then y toma un argumento.
1 var promise = new Promise(function(resolve,reject){ 2 resolve("Estaba resuelto y puedes verme cuando usas el método."); 3 }); 4 promise.then(value => console.log(value));
  • catch: devuelve una promesa y se ocupa de las operaciones rechazadas. Es muy útil cuando se trata de depurar o mostrar errores.
1 var promise = new Promise(function(resolve,reject){ 2 reject("Me rechazaron y puedes verme cuando usas el método catch."); 3 }); 4 promise.catch(error => console.log(error));

Async/await

  • Async/await es una forma de escribir código asíncrono.
  • Async es una función de JavaScript y puede contener una expresión await.
  • Await pausa la ejecución de la función asíncrona y espera el resultado de una promesa.

☝️ Recuerda que las expresiones await sólo son válidas dentro de funciones asíncronas. Si las usas fuera, tendrás un error de sintaxis.

1function returnedPromiseHere() { 2 return new Promise((resolve, reject) => { 3 setTimeout(() => { 4 resolve("Yo soy las imágenes que vienen de la base de datos."); 5 }, 1000); 6 }); 7} 8async function useAsyncFunction() { 9 console.log("Soy una tarea rapida"); 10 var result = await returnedPromiseHere(); 11 console.log(result); 12 console.log("Tuve que esperar a que terminara"); 13} 14useAsyncFunction(); 15 16/*RESULTADO EN CONSOLA: 17 >"Soy una tarea rápida" 18 //después de 1 segundo... 19 >"Yo soy las imágenes que vienen de la base de datos." 20 >"Tuve que esperar a que terminara" 21*/

Async se vuelve poderoso cuando hay varios pasos en acción:

1function promise1() { 2 return new Promise((resolve, reject) => { 3 setTimeout(() => { 4 resolve("Estoy resuelto como 1"); 5 }, 100); 6 }); 7} 8function promise2() { 9 return new Promise((resolve, reject) => { 10 setTimeout(() => { 11 resolve("Estoy resuelto como 2"); 12 }, 200); 13 }); 14} 15function promise3() { 16 return new Promise((resolve, reject) => { 17 setTimeout(() => { 18 resolve("Estoy resuelto como 3"); 19 }, 300); 20 }); 21} 22async function handlingAllPromises() { 23 var first = await promise1(); 24 var second = await promise2(); 25 var third = await promise3(); 26 27 console.log(first); 28 console.log(second); 29 console.log(third); 30} 31handlingAllPromises();

En el ejemplo anterior, en lugar de esperar una promesa en cada nueva línea, podríamos usar el método Promise.all y esperar a que se cumplan todas las promesas.

1 var [first, second, third] = await Promise.all([promise1(), promise2(), promise3()]);

También puedes hacer funciones asíncronas como funciones de flecha(arrow).

1const handlingAllPromises = async () => { 2 var [first, second, third] = await Promise.all([promise1(), promise2(), promise3()]); 3 4 console.log(first); 5 console.log(second); 6 console.log(third); 7}

¿Cómo manejar errores en funciones asíncronas?

Una buena manera de manejar los errores en las funciones asíncronas es usar las sentencias try... catch.

1async function handeErrors() { 2 let msg; 3 try { 4 msg = await promise1(); //Nota que este método ya está escrito en su aplicación. 5 console.log(msg); 6 } catch(err) { 7 console.log(err); 8 } 9}

Fetch API se basa en la promesa. ¿Adivina qué? ¡Puedes usarlo en tus funciones asíncronas también!

1async function fetchData(endpoint) { 2 const response = await fetch(endpoint); //nota el uso de fetch api 3 let data = await res.json(); 4 data = data.map(user => user.ID); 5 console.log(data); 6} 7 8fetchData(http://dummyData/api/allUsers); //este es un ejemplo de endpoint 9 10/*RESULTADO EN CONSOLA: 11 >[1, 2, 3, 4] //Aquí obtenemos todos los usuarios ID de la base de datos 12*/

En conclusión

Tienes la capacidad de crear aplicaciones web increíbles y más rápidas. Además, los usuarios y las tareas más rápidas ya no tienen que esperar a que finalicen las tareas lentas, gracias a la programación asíncrona.