Diferencias entre JavaScript síncrono y asíncrono: Promesas y más

Go to Homepage

JavaScript síncrono y asíncrono: una breve introducción

JavaScript es un lenguaje de programación muy popular utilizado en la web para agregar interactividad y dinamismo a las páginas. Una de las características que lo hace único es su capacidad para ejecutarse tanto de forma síncrona como asíncrona.

Cuando el código se ejecuta de forma síncrona, significa que sigue una secuencia de instrucciones de arriba hacia abajo. Cada línea de código se ejecuta uno a uno y espera a que la anterior termine antes de avanzar a la siguiente. Si hay una tarea que lleva tiempo, todo el proceso se detiene y espera hasta que finalice esa tarea.

Por ejemplo, supongamos que tienes un sitio web con un formulario de contacto. Cuando el usuario envía el formulario, el servidor necesita procesar los datos y enviar un correo electrónico de respuesta. Si este proceso se ejecutara de forma síncrona, el usuario tendría que esperar a que el proceso de envío de correo electrónico finalice antes de que pueda ver la página de confirmación. Esto no sería una gran experiencia para el usuario y podría hacer que pierdas visitantes en tu sitio web.

Por otro lado, cuando el código se ejecuta de forma asíncrona, significa que las tareas se realizan en segundo plano. En lugar de esperar a que se complete cada tarea, el código continúa ejecutándose y puedes realizar otras tareas mientras esperas que las tareas en segundo plano se completen. Esto significa que las páginas cargan más rápidamente y los usuarios pueden seguir interactuando con el sitio mientras se realizan otras tareas en segundo plano.

Un ejemplo común de código asíncrono es el manejo de eventos. Supongamos que tienes un botón en tu sitio que carga más información. Cuando el usuario hace clic en el botón, el código ejecuta una tarea en segundo plano para obtener más información y luego actualiza la página para mostrar la información nueva. Mientras se cargan los datos, el usuario puede seguir usando la aplicación sin interrupciones.

El código JavaScript puede ejecutarse de forma síncrona o asíncrona, y ambos tienen sus usos. La ejecución síncrona se utiliza para tareas simples, mientras que la ejecución asíncrona se utiliza para tareas más complejas que toman más tiempo. A menudo, se usan juntas para obtener el mejor resultado posible y brindar la mejor experiencia al usuario.

Cómo trabajar con promesas en JavaScript

En el artículo anterior hablamos sobre las diferencias entre JavaScript síncrono y asíncrono, y mencionamos las promesas como una forma de manejar la asincronía en nuestro código. En esta sección, profundizaremos en el tema de las promesas y cómo podemos trabajar con ellas en nuestro código de JavaScript.

Una promesa es un objeto que representa un valor que puede estar disponible en el futuro. Esta puede estar en cuatro estados: pendiente, resuelta, rechazada o en espera. Para crear una promesa, utilizamos la clase Promise que toma una función como parámetro. Esta función, a su vez, tiene dos parámetros: resolve y reject. El resolve se utiliza para indicar que la promesa se ha resuelto exitosamente, mientras que reject se utiliza para indicar un error en la promesa.

const miPromesa = new Promise((resolve, reject) => {
    // Hacer alguna tarea asincrónica
    if (tareaExitosa) {
        resolve(resultados);
    } else {
        reject("Hubo un error");
    }
});

Una vez que creamos una promesa, podemos trabajar con ella utilizando los métodos then, catch y finally. El método then se utiliza para manejar el caso de la promesa resuelta, mientras que catch se utiliza para manejar el caso de la promesa rechazada. Por último, finally se utiliza para realizar alguna acción sin importar el resultado de la promesa.

miPromesa
    .then((resultados) => {
        // Hacer algo con los resultados
    })
    .catch((error) => {
        // Manejar el error
    })
    .finally(() => {
        // Hacer algo después de la promesa
    });

Es importante mencionar que cuando utilizamos el método then, este devuelve una nueva promesa. Esto significa que podemos encadenar múltiples then para trabajar con los resultados de forma secuencial.

miPromesa
    .then((resultados) => {
        // Hacer algo con los resultados
        return siguienteTarea(resultados);
    })
    .then((resultadosSiguienteTarea) => {
        // Hacer algo con los resultados de la siguiente tarea
    })
    .catch((error) => {
        // Manejar el error
    })
    .finally(() => {
        // Hacer algo después de la promesa
    });

Las promesas son una forma de manejar la asincronía en nuestro código de JavaScript. Podemos utilizar la clase Promise para crear una promesa, y luego trabajar con ella utilizando los métodos then, catch y finally. Al encadenar múltiples then, podemos trabajar con los resultados de forma secuencial. Esperamos que esta sección te haya ayudado a entender cómo trabajar con promesas en JavaScript.

Ventajas y desventajas del uso de promesas en comparación con callbacks

En el desarrollo de aplicaciones web, el manejo de operaciones asíncronas es esencial. En este sentido, JavaScript ofrece dos métodos para manejar dichas operaciones: callbacks y promesas. Ambos métodos tienen sus ventajas y desventajas, pero en este artículo nos enfocaremos en comparar las ventajas y desventajas de las promesas en relación a los callbacks.

Ventajas

Código más legible

Al utilizar promesas, el código es más fácil de leer y entender. En vez de anidar múltiples callbacks, las promesas permiten encadenar funciones para realizar una serie de operaciones de manera más clara y ordenada. Además, el uso de las palabras clave then y catch hace que el flujo del código sea mucho más fácil de seguir.

Manejo de errores

Una de las mayores ventajas de las promesas es el manejo de errores. Al utilizar callbacks, es fácil caer en el callback hell, en donde los errores pueden ser difíciles de detectar y manejar. En cambio, con las promesas, es fácil manejar errores de manera centralizada utilizando el método catch.

Funciones reutilizables

Con las promesas es posible crear funciones reutilizables que pueden ser utilizadas en múltiples partes de la aplicación. Esto es especialmente útil para funciones que realizan operaciones asíncronas, ya que el manejo de errores y el flujo de la lógica pueden ser encapsulados en una sola función.

Desventajas

Curva de aprendizaje

Las promesas pueden tener una curva de aprendizaje un poco más pronunciada que los callbacks debido a la sintaxis específica que utilizan. Además, puede ser difícil entender cómo funcionan las promesas bajo el capó y cómo se manejan las operaciones asíncronas.

Es fácil olvidar el return

Una de las mayores trampas al utilizar promesas es olvidar el return dentro de una función que devuelve una promesa. Si se olvida el return, la promesa no se resolverá correctamente.

Ejemplo

A continuación, se muestra un ejemplo de cómo utilizar promesas para realizar una operación asíncrona:

function getJSON(url) {
    return new Promise((resolve, reject) => {
        const request = new XMLHttpRequest();
        request.open("GET", url);
        request.onload = function () {
            if (request.status === 200) {
                resolve(JSON.parse(request.responseText));
            } else {
                reject(new Error("Failed to load data"));
            }
        };
        request.onerror = function () {
            reject(new Error("Network error"));
        };
        request.send();
    });
}

getJSON("https://jsonplaceholder.typicode.com/todos/1")
    .then((data) => console.log(data))
    .catch((error) => console.error(error));

En este ejemplo, la función getJSON devuelve una promesa que realiza una solicitud HTTP para obtener datos JSON de un servicio web. La promesa se resuelve correctamente si la solicitud se realiza con éxito y se devuelve el resultado. Si hay algún error, se maneja con el método catch y se muestra un mensaje de error en la consola del navegador.

Las promesas son una forma más moderna y elegante de manejar operaciones asíncronas en JavaScript que ofrecen ventajas significativas en comparación con callbacks. Si bien hay una curva de aprendizaje, una vez que se entienden las promesas, pueden hacer que el código sea más fácil de leer, manejar errores de manera centralizada y crear funciones reutilizables.

Cómo manejar errores en promesas

Cuando trabajas con promesas en JavaScript, es importante saber cómo manejar errores adecuadamente. Uno de los mayores beneficios de las promesas es que te permiten manejar errores de una manera más clara y concisa que con callbacks.

En promesas, hay dos maneras principales de manejar errores: usando el método catch() y devolviendo una promesa rechazada.

La forma más común de manejar errores en promesas es usando el método catch(). Este método se encarga de atrapar cualquier error que ocurra dentro de la promesa y ejecutar una función en respuesta. Por ejemplo, si tienes una promesa que hace una solicitud HTTP y esa solicitud devuelve un error, puedes manejar ese error con el método catch() de la siguiente manera:

fetch("https://api.example.com/data")
    .then((response) => response.json())
    .catch((error) => console.error(error));

En este ejemplo, si la solicitud HTTP falla, el método catch() ejecutará la función que pasa como argumento y registrará el error en la consola del navegador.

También puedes devolver una promesa rechazada para manejar errores en promesas. Esto te permite controlar más exactamente el flujo de la promesa y proporcionar información detallada sobre el error que ocurrió. Por ejemplo, si tienes una promesa que hace una operación de cálculo compleja, puedes manejar errores y devolver una promesa rechazada de la siguiente manera:

function makeCalculation(value) {
    return new Promise((resolve, reject) => {
        if (typeof value === "number") {
            resolve(value * 2);
        } else {
            reject(new Error("El valor no es un número."));
        }
    });
}

makeCalculation("no es un número")
    .then((result) => console.log(result))
    .catch((error) => console.error(error.message));

En este ejemplo, la función makeCalculation() devuelve una promesa que resuelve el doble del valor de entrada si el valor es un número y rechaza la promesa con un objeto Error con un mensaje descriptivo si no lo es. Si el valor no es un número, el método catch() captura el error y registra el mensaje de error en la consola.

Es importante manejar los errores adecuadamente en promesas para evitar errores y hacer que tu código sea más escalable. Con un manejo adecuado de errores, puedes hacer que tus promesas sean más confiables y expresivas, lo que te permite escribir código más claro y fácil de mantener.

Cómo combinar varias promesas en una sola

Si eres programador, seguramente has escuchado el término “promesas” en relación con JavaScript. Las promesas son una forma de controlar el flujo de código asíncrono y asegurarse de que una secuencia de tareas se realice en orden. Pero, ¿qué sucede cuando necesitas combinar varias promesas en una sola? ¡Eso es lo que vamos a abordar en este artículo!

Para empezar, debemos entender que las promesas se representan mediante objetos que pueden tener tres estados: pendiente, resuelta o rechazada. Si una promesa se resuelve correctamente, se ejecuta una función de resolución. Si la promesa es rechazada, se ejecuta una función de rechazo.

Si necesitamos ejecutar varias promesas a la vez y esperar a que todas se resuelvan, podemos utilizar el método Promise.all(). Este método toma un array de promesas, las ejecuta en paralelo y devuelve una nueva promesa que se resuelve cuando todas las promesas del array original se hayan resuelto.

Veamos un ejemplo:

const promesaUno = new Promise((resolve, reject) => {
    setTimeout(() => {
        console.log("La promesa uno se ha resuelto");
        resolve("uno");
    }, 2000);
});
const promesaDos = new Promise((resolve, reject) => {
    setTimeout(() => {
        console.log("La promesa dos se ha resuelto");
        resolve("dos");
    }, 1000);
});
const promesaTres = new Promise((resolve, reject) => {
    setTimeout(() => {
        console.log("La promesa tres se ha resuelto");
        resolve("tres");
    }, 5000);
});

Promise.all([promesaUno, promesaDos, promesaTres])
    .then((resultados) => {
        console.log("Todas las promesas han sido resueltas");
        console.log(resultados);
    })
    .catch((error) => {
        console.error(error);
    });

En este ejemplo, creamos tres promesas que se resolverán después de 2, 1 y 5 segundos respectivamente. Luego, pasamos las tres promesas a Promise.all() y esperamos a que se resuelvan todas. En este caso, la consola imprimirá:

La promesa dos se ha resuelto
La promesa uno se ha resuelto
La promesa tres se ha resuelto
Todas las promesas han sido resueltas
[ 'uno', 'dos', 'tres' ]

Nota que aunque la segunda promesa se resuelve antes que la primera en nuestro código, JavaScript ejecuta las promesas en el orden en que se declaran en el array de Promise.all(). Por lo tanto, la consola muestra primero el mensaje “La promesa dos se ha resuelto” porque es la primera en el array de Promise.all().

Ahora que hemos visto cómo utilizar Promise.all(), es importante destacar que si una de las promesas en el array es rechazada, la nueva promesa que devuelve Promise.all() se rechazará inmediatamente.

Las promesas son una poderosa herramienta para controlar el flujo de código asíncrono en JavaScript. Si necesitas combinar varias promesas en una sola, utiliza Promise.all() para ejecutarlas en paralelo y esperar a que todas se resuelvan. ¡Prueba experimentar con tus propias promesas y descubre lo que puedes hacer con ellas!

Otros Artículos