Como funciona la asincronía en Javascript

Curso de Javascript Online. Desde cero.

14.  
19.  
25.  
34.  

Por 9.99€ al mes tendrás acceso completo a todos los cursos. Sin matrícula ni permanencia.

En este artículo veremos varios conceptos relacionados con la asincronía en Javascript.

Asincronía vs Sincronía

En programación, nos referimos a procesos síncronos a aquellos que se realizan de manera secuencial, uno detrás de otro, sin simultaneidad.

Los procesos asíncronos, sin embargo, se realizan en paralelo, todos al mismo tiempo.

Javascript es un lenguaje síncrono. Todo lo que hagas se ejecutará en el mismo hilo. Javascript tiene un único hilo, por lo tanto, no ofrece la posibilidad de simultaneidad en su ejecución.

Entonces… ¿cómo se realizan los procesos asíncronos en Javascript?

Asincronía en Javascript

Fíjate en el siguiente código. ¿Puedes predecir que se verá en la consola del navegador cuando lo ejecutes?

<script>

    console.log(1);
    setTimeout(function(){ console.log(2) }, 0);
    console.log(3);

</script>

La respuesta es: 132.

Para comprender la respuesta, debemos entender el siguiente gráfico:

Como funciona la asincronía en Javascript 1

Call Stack

Javascript tiene una pila de contextos que se van ejecutando (call stack). Cada función que ejecutamos tiene su propio contexto. Los contextos se van colocando uno tras otro en esta pila.

Web API

Cuando quieres lanzar un proceso asíncrono, este es gestionado por la API del navegador, que sí que funciona de manera asíncrona (aunque la especificación de ECMA script, no).

Callback Queue

Al terminar el proceso asíncrono de la Web API, pasa a ser gestionado por el Callback Queue, que espera a que el callstack no tenga ningún contexto en ejecución para ejecutarse

Explicación paso a paso

  1. Se ejecuta la línea 1, que hace un log del número 1.
  2. Se ejecuta la línea 2. En esta línea creamos un evento timer con la función setTimeout. Éste nos abriría un subproceso asíncrono donde se esperaría el tiempo especificado en la función (en este caso 0s) y al acabar éste se notificaría como terminado a la callback queue.
  3. Se ejecuta la línea 3, que hace un log del número 3.
  4. Finalmente al estar el stack vacío, pasamos a ejecutar los eventos guardados en la cola de eventos (callback queue). En este caso será el código registrado por el setTimeout de la línea 2, que hace un log del número 2.

¿Quieres verlo funcionando? Aquí tienes una escenificación de lo que está pasando.

Como funciona la asincronía en Javascript 2

Anonymous es la función anónima que estámos disparando en el setTimeout.

En el callback queue no todos los eventos tienen la misma prioridad. Por ejemplo, una promesa tiene más prioridad que un timeout.

¿Que es una promesa?

Una Promise es un objeto que nos permite manejar código asíncrono. Por ejemplo, una llamada al método fetch devuelve una Promise. Este objeto devuelto nos permite usar el método then. El método then, nos permite ejecutar un código cuando la promesa haya concluído.

Tiene 3 estados:

  • PENDING
  • FULLFILLED
  • REJECTED

Ejemplo de una promesa que siempre será completada:

const task = new Promise((resolve, reject) => {
    // tarea
    resolve('tarea finalizada');
})

task.then(result => {
    console.log(result)
})

El método resolve indica que la promesa ha ido bien.

Ejemplo de promesa que siempre será rechazada:

const task = new Promise((resolve, reject) => {
    // tarea
    reject('mensaje de error');
})

task.then(result => {
    console.log(result); // no se ejecuta
}, err => {
    console.log('error: ' + err);
})

El método reject indica que la promesa tenía errores.

finally

Permite ejecutar código tanto si hay error como si no lo hay

<script>
    const task = new Promise((resolve, reject) => {
        // tarea
        reject('mensaje de error');
    })

    task.then(result => {
        console.log(result); // no se ejecuta
    }, err => {
        console.log('error: ' + err);
    }).finally(() => {
        console.log('Este código se ejecutará tanto si ha ido bien como si ha ido mal')
    })
</script>

Usando la API async/await en lugar de la API de Promise (then)

Async/await es sugar-sintax para gestión de promesas.

Vamos a realizar la misma petición del ejemplo anterior, pero ahora en lugar de utilizar la api de Promise (método then ) para procesar la respuesta vamos a utilizar los modificadores async y await.

const b = () => new Promise(resolve => {
    setTimeout(() => {
        console.log('tarea');
        resolve();
    }, 3000)
})

const a = async () => {
    await b();
    console.log('llegamos');
}

a();
  • El modificador await siempre debe estar dentro de una función con el modificador async, o daría error. Este modificador hace que hasta que no termine la promesa que le hemos pasado, las siguientes líneas de código no se ejecuten.
  • El modificador async convierte la función en una Promise.

A continuación veremos como usar async/await para gestionar la promesa que devuelve el método fetch.

const getPokemon = async () => {
    const result = await  fetch('https://pokeapi.co/api/v2/pokemon/ditto/');
    const res = await result.json();
    console.log(res);
};
getPokemon();

Por 9.99€ al mes tendrás acceso completo a todos los cursos. Sin matrícula ni permanencia.