¿Cómo es posible que Javascript sea asyncrono y mono-hilo al mismo tiempo? Esto es posible mediante el loop de eventos y la message queue. El Loop de Eventos (que realmente está fuera del runtime de JS, es decir, es algo propio del navegador), está continuamente chequeando la call stack, para verificar si hay alguna función que necesite ejecutar código. Mientras hace esto, añade cualquier evento que tenga encolado (sólo cuando la call stack esté vacia).

 

Por ejemplo, analizando el siguiente código:

const bar = () => console.log('bar')
const baz = () => console.log('baz')
const foo = () => {
  console.log('foo')
  setTimeout(bar, 0)
  baz()
}
foo()

Cuando se ejecuta el código, lo primero que ocurre es que foo es ejecutada. Por tanto foo pasa a la cola de ejecución creando su propio execution context. Dentro de foo(), la primera instrucción imprime "foo" en la consola, y posteriormente mediante setTimeOut, se pasa bar como función de callback.

setTimeOut es una función especíal, porque hace que el navegador inicie un timer, y que cuando se alcance el limite definido, esta función pasa al MESSAGE QUEUE. Aquí está la clave. La ejecución de bar (el callback) no es inmediata cuando finalice el timer, si no que el loop de eventos, una vez recoja este callback (que es pasado por el navegador cuando el timer finaliza), lo pasará al execution context, cuando esté esté vacio.

Por tanto la salida de ejecución de este programa será:

> foo
> baz
> bar

Promesas: (extraido de Developer Mozilla) es un objeto que representa la terminación o el fracaso eventual de una operación asíncrona. Una promesa puede ser creada usando su constructor. Sin embargo, la mayoría de la gente son consumidores de promesas ya creadas devueltas desde funciones. Las promesas surgen con el objetivo de solucionar el llamada "callback hell", que se producía en JS cuando se anidaban sucesivas callback, lo cual hacía que fuese muy complicado saber el orden en el que se ejecutaría el código (callback hell).

  • Defining promises:

       const getRecipeIDs = new Promise((resolve, reject) => {
            setTimeout(() => {
                resolve ([523, 42, 123, 230]);
            }, 1500);
        });
        
        const getRecipe = idRecipe => {
            return new Promise ((resolve,reject) => {
                setTimeout(id => {
                    let recipe = {
                        name: 'Fresh Tomato',
                        publisher: 'Google',
                    };
                    resolve(recipe);
                }, 1500, idRecipe);
            });
        };
        
        const getRelated = publisher => {
            return new Promise ((resolve,reject) => {
                setTimeout(pub => {
                    let recipes = [
                    { name: 'Fresh Tomato', publisher: 'Google',},
                    { name: 'Baked Potatos', publisher: 'Google',},
                    { name: 'Pizza Diavola', publisher: 'Google',},
                    { name: 'Boiled Eggs', publisher: 'Google',},
                    ];
                    resolve(recipes);
                }, 1500, publisher);
            });
        };
  • Consuming promises in ES6 (anidamiento):

       getRecipeIDs
        .then (IDs => {
            console.log(IDs);
            return getRecipe(IDs[1]);
        })
        .then (recipe => {
            console.log(recipe);
            return getRelated(recipe.publisher);
        })
        .then (recipes => {
            console.log(recipes);
        })
        .catch (() => {
            console.log();
        })

  • Consuming promises with Async/Await (ES2017):

        async function getRecipeIDsAW () {
            const IDs = await getRecipeIDs;
            console.log(IDs);
            const recipe = await getRecipe(IDs[1]);
            console.log(recipe);
            const related = await getRelated(recipe.publisher);
            console.log(related);
        }
        getRecipeIDsAW();

Fetch with Async / Await (extraido de developer mozilla): la API Fetch proporciona una interfaz JavaScript para acceder y manipular partes del canal HTTP, como peticiones y respuestas. También provee un método global fetch() que proporciona una forma fácil y lógica de obtener recursos de forma asíncrona por la red. 

Este tipo de funcionalidad se conseguía previamente haciendo uso de XMLHttpRequest. Fetch proporciona una mejor alternativa que puede ser empleada fácilmente por otras tecnologías como Service Workers. Fetch también aporta un único lugar lógico en el que definir otros conceptos relacionados con HTTP como CORS y extensiones para HTTP.

        async function getTemperaturesAW_ID (id) {
            try {
                let content = await fetch(`https://api.codetabs.com/v1/proxy?quest=https://www.metaweather.com/api/location/${id}/`);
                let data = await content.json();
                let cons_weather = data.consolidated_weather[0];
                return {
                    name: data.title,
                    min_temp: cons_weather.min_temp.toFixed(0),
                    max_temp: cons_weather.max_temp.toFixed(0),
                }
                
            } catch (error) {
                console.log(error);
            }
        }
        getTemperaturesAW_ID(44418)
        .then(data => {
            console.log(`Today, in ${data.name} the min temperature will be ${data.min_temp}, and the max temperature will be ${data.max_temp}`);
        });
        getTemperaturesAW_ID(766273);
        getTemperaturesAW_ID(2487956);