Programación síncrona vs. asíncrona

Programación síncrona vs. asíncrona

Al iniciar el estudio en algún lenguaje de programación, se nos introduce en dos conceptos muy importantes para estructurar un código completo: código síncrono y asíncrono.

Como breve explicación, el código síncrono se lee y ejecuta desde la primera hasta la última línea, en orden. El código asíncrono, en cambio, no respetará necesariamente el orden de las líneas de código y puede tener funciones que se leen y ejecutan de forma simultánea o aleatoria.

Ahora que entendemos los conceptos básicos, entremos en más detalles en este artículo sobre sus diferencias, ventajas y desventajas. A continuación veremos algunos ejemplos de cómo esto se aplica en la práctica.

Programación síncrona

La programación síncrona es el tipo predeterminado de ejecución de código y es lo que vemos inicialmente en nuestros primeros proyectos. Su principal ventaja es que es de fácil lectura y su sintaxis es más sencilla. Sin embargo, en la programación síncrona el código se interrumpe mientras que el retorno de la función no se completa. En la práctica, esto significa que la experiencia del usuario dentro de su aplicación se interrumpiría al iniciar una ejecución prolongada. La ejecución del código quedaría "atascada" al realizar cualquier función, lo cual es su principal desventaja.

Un ejemplo de esto es que, al buscar "celular" en una página de comercio electrónico, la aplicación se congelaba hasta que se renderizaban los productos, impidiendo que el usuario abra el menú, edite el carrito de compras y navegue por la página. El código síncrono no debe usarse para funciones que toman una cierta cantidad de tiempo y no debe usarse para esperar a que regresen los servidores externos.

Con esta desventaja que trae la programación síncrona, se abrió espacio para nuestro segundo concepto, la programación asíncrona.

Programación asíncrona

Como mencionamos anteriormente, en la programación asíncrona el código no respetará un orden jerárquico o secuencial. Es decir, el código llamará a la función pero continuará ejecutando las otras líneas mientras espera la respuesta de la función. La asincronía es una buena idea cuando es probable que la función tarde algún tiempo en ejecutarse, como las solicitudes de datos al servidor o la función setTimeout de JavaScript.

Ejemplo de algo en la vida cotidiana: Cuando enviamos un mensaje, podemos hacer otras cosas mientras esperamos la respuesta, como enviar otros mensajes, enviar videos, audios o incluso bloquear al usuario. Funciona como poner algo que “escucha” cuando la función recibe el retorno, sin embargo, sigue ejecutando el resto del código. Desde el momento en que la función recibe el retorno, hace lo que espera la funcionalidad. Si la función que lleva tiempo fuera, por ejemplo, subir una foto, podrías enviar más mensajes mientras la foto aún no se ha cargado.

A continuación se muestra un gráfico simple que representa cómo funcionaría la operación síncrona y asíncrona en un sitio de comercio electrónico.

Como podemos ver en nuestro ejemplo, la programación asíncrona permite una mayor fluidez en la experiencia del usuario y agilidad en la ejecución de las funcionalidades de la aplicación. Un código bien pensado se hace con la idea de que no importa el orden de lectura del código, lo que, en consecuencia, garantiza un mejor rendimiento cuando se está ejecutando.

Sabiendo esto, es claro que esta funcionalidad es extremadamente útil, sin embargo, ¿cómo se aplica esto en la práctica? Eso es lo que veremos a continuación usando algunos ejemplos en JavaScript.

Programación síncrona en la práctica

Creamos una lógica simple que sigue la ejecución del código en el orden en que fue escrito, hacemos una suma y luego escribimos el resultado en la consola. Al ejecutar nuestro código, la salida saldrá en este orden:

En el primer “registro” tenemos (a = 1, b = 2), en el segundo “registro” tenemos (a = 3, b = 2), que, finalmente, escribe la suma de las variables en la terminal en orden.

Con eso ya entendemos los conceptos básicos de la ejecución de código y finalmente podemos pasar a la programación asíncrona.

Programación asíncrona en la práctica

En JavaScript, tenemos algunas formas de transformar y tratar el código asíncrono: podemos usar devoluciones de llamada, funciones asíncronas, promesas o 'async/await'. Todas estas aplicaciones tienen sus respectivas ventajas y usos, pudiendo incluso utilizarse juntas. Así que empecemos con el ejemplo.

Inicialmente, para comprender los conceptos básicos de la asincronía, usemos la función setTimeout, que obliga a una determinada función a esperar una determinada cantidad de tiempo antes de ejecutarse, simulando el retraso en el retorno:

Al ejecutar el código, nuestra salida se vería así:

Esta vez, la función setTimeout ejecutó el archivo console.log solo 3 segundos después de su llamada y, por lo tanto, la variable "a" ya se había cambiado a "3", lo que resultó en dos resultados donde (a = 3 y b = 2) .

Esto, aunque mejora la ejecución del código, hace que todo sean un poco confuso. Para facilitar el uso de la ejecución asíncrona, apareció la propiedad “async/await”, que trata la ejecución asíncrona como síncrona. Algo que quedará más claro a continuación.

¿Cómo usar async/await?

Cuando necesitamos esperar un resultado para continuar leyendo el código, usamos await, una propiedad que espera el regreso de la función aunque tarde un tiempo, y solo después del regreso continúa la ejecución del código.

Primero, creemos una función asíncrona que devuelva una Promise y tarda 3 segundos en ejecutarse:

Antes de explicar cómo usamos await y async, es importante comprender el concepto y el funcionamiento de una “Promesa”. Cuando creamos una Promise, en definitiva, hacemos una función que tiene su retorno prometido, pero aún no realizado. Un ejemplo de esto sería un restaurante que recibe tu pedido, pero solo entrega el plato después de un tiempo, cuando está listo.

En nuestro ejemplo, creamos la función takesLong. Necesitamos poner la palabra async antes de llamarlo con arrow function. Esta palabra se usa para crear funciones asincrónicas y, solo entonces, podemos usar la propiedad await en nuestro alcance. En el caso anterior, al llamar a Promise, pasamos una función que tarda 3 segundos en ejecutar el parámetro resolve, que devuelve la promesa y finalmente finaliza su ejecución.

Posteriormente se creó la función executionEnvironment, que servirá como entorno de alcance. Cuando llamamos a una función asíncrona con await, garantizamos que el código solo continuará cuando tengamos el retorno de la función, es decir, como cada función tarda tres segundos en ejecutarse, el código debe tardar seis segundos en leerse por completo y el output sería así:

Hasta ahora hemos aprendido a usar Promises y código asíncrono. Pero todavía tenemos la pregunta de cómo hacer que dos o más funciones se ejecuten al mismo tiempo. Para hacer esto, ejecutaremos Promises al mismo tiempo. Ve más a continuación.

Promises ejecutadas simultáneamente

Ejecutar Promises simultáneamente es genial cuando hacemos solicitudes que no están relacionadas entre sí, reduce el tiempo total de espera y mejora el flujo de ejecución. Para empezar, debemos eliminar la espera de nuestra función, ya que ahora no vamos a esperar la devolución. La función anterior se verá así:

Cuando eliminamos el await, la función devuelve una promise de que llegará el retorno. Lo que hicimos fue almacenar esta promise en una constante y usamos la funcionalidad Promise.all(), una función que recibe una matriz, funcionando como una lista y dispara todas las promises dentro de la matriz y espera su retorno. Finalmente, usamos .then para escribir en la terminal cuando el proceso Promise.all() finaliza correctamente.

Nuestro output será:

Como podemos ver, fue en el mismo orden que el anterior, pero tardó tres segundos en total en ejecutar el código, siendo dos veces más rápido que el método anterior. Después de comprender estas funcionalidades, ya seremos capaces de aplicar estos conceptos en nuestras aplicaciones.

Ambos conceptos se usarán constantemente a medida que evolucionemos como programadores, pero ¿cuándo debemos usar cada concepto? Después de seguir los ejemplos anteriores, llegamos a la conclusión de que una función asíncrona debe ser tratada como síncrona cuando no interfiere en el funcionamiento general de la aplicación.

La programación asíncrona, con Callbacks y Promises, debe utilizarse para devoluciones que llevarán algún tiempo o que afecten directamente al funcionamiento de la aplicación. Por lo tanto, comprender estas formas de ejecución de código es muy importante y debe practicarse en la vida diaria del desarrollador.

⚠️
Las opiniones y comentarios emitidos en este artículo son propiedad única de su autor y no necesariamente representan el punto de vista de Revelo.

Revelo Content Network da la bienvenida a todas las razas, etnias, nacionalidades, credos, géneros, orientaciones, puntos de vista e ideologías, siempre y cuando promuevan la diversidad, la equidad, la inclusión y el crecimiento profesional de los profesionales en tecnología.