Async / Await?

Este post es una transcripción para estudiar de este vídeo de Fireship 🔥🔥🔥

Cómo funciona el event loop?

Tanto el browser como NodeJS corren siempre un hilo de proceso.

En una vuelta van a correr todo el código sincrónico. Pueden haber eventos asincrónicos que se correran luego como macrotareas y micro tareas.

Las macro tareas se ejecutan al comienzo del próximo ciclo del hilo. Las microtareas (p.e. promesas) se ejecutan antes de que el ciclo presente termine.

Promesas

fetch: es un API del navegador que ‘pide’ a una dirección y nos deja ver cuál es la respuesta (como una promesa).

const url = 'https://jsonplaceholder.typicode.com/todos/1'
const promesa = fetch(url)

Promesa no va a tener el valor de la respuesta hasta que no sea resuelta (es asincrónica) por lo que tenemos que ponerla en cola (queue, o esperar a que se resuelva)

const promesa = fetch(url);
promesa.then( res => res.json() );
// mapear a json tambien es una promesa porque puede
// tomar mucho tiempo

Una caracteristica de esta promesas encadenadas (primero el fetch, luego el json, luego otra cosa) es que se pueden ‘desencadenar’

promesa.
  then( res => res.json() ).
  then( user => console.log('😀', user.title ) )

Por ejemplo,  atrapar un error durante la resolución de las promesas es una promesa especial llamada catch

promesa.
  then( res => res.json() ).
  then( user => console.log('😀', user.title ) ).
  catch(err => console.error('😰',err))

Non-Blocking code

Imagina esto:

const Bloqueadora = () => {
  let i =0;
  while(i<1000000000) i++;
  return 'ya terminé de entorpecer tu hilo de proceso'
}

Esta tarea frenará el hilo de proceso hasta que culmine de contar. Como es algo que nos va a retrasar, podemos hacerla una promesa, y que retorne luego en el futuro

const Bloqueadora = () => {
  return new Promise((resolve, reject) => {
    let i =0;
    while(i<1000000000) i++;
    return 'ya terminé de entorpecer tu hilo de proceso'
  })
}

Pero hacerlo de esta forma está metiendo a la promesa en el ciclo principal. Lo que necesitamos que esté en el ciclo es la resolución  de la promesa.

const Bloqueadora = () => {
  return Promise.resolve().then( x => {
  let i =0;
  while(i<1000000000) i++;
  return 'ya terminé de entorpecer tu hilo de proceso'
})

Async / await y la magia de ES6 🔮🔮🔮

async y await son una forma de hacer más bonita la sintaxis de promesas, sobre todo cuando hay promesas encadenadas:

promiseHell.png

para evitar esto se pueden crear funciones que retornen promesas así:

const getFruta = async (name) => {
  const fruta = {
  pineapple: '🍍',
  peach: '🍑',
  strawberry: '🍓'
  }
  await delay(1000)
  return fruta[name];
}

const hasJugo = async () => {
  const a = await getFruta('pineapple');
  const b = await getFruta('peach');
  return [a, b];
}

Pero esto introduce otro problema, que ahora hasJugo está creando dos promesas consecutivas, en vez de ser paralelas. Para hacer promesas concurrentes no hay magia :(:

const hasJugo = async () => {
  const a = getFruta('pineapple');
  const b = getFruta('peach');
  return Promise.all([a, b]);
}

Ese Promise.all hace que todas las promesas se realicen de manera concurrente.

Normas de buena educación

Es de buena educación que toda la promesa tenga manejo de posibles errores:

const hasJugo = async () => {
try{
  const a = getFruta('pineapple');
  const b = getFruta('peach');

  throw 'la fruta estaba rancia!!!'

  return Promise.all([a, b]);
} catch(err){
    console.error(☠,err)
    //y aca se puede o lanzar un error ó
    //retornar un mensaje
    //return 'el cliente no se va a dar cuenta'
    //throw 'sabe a 💩'
    //return: no modifica la lógica del programa
    //throw: el programa tendrá que hacer un catch
  }
}

Tips de cierre:

hacer await dentro de un map, o cualquiera de esas higher order functions no espera a la resolución de la promesa, la deja ejecutando concurrentemente.

Si se quiere que un loop espere a la resolución de cada promesa antes de continuar con la siguiente se debe hacer un ciclo for:

const fruits = ['peach', 'pineapple', 'strawberry']
const getPromesa = async () => {
  for(const f of fruits){
    const emoji = await getFruta(f);
    console.log(emoji)
  }
}

si se quiere que la cosa sea concurrente, se puede usar el map (que dije que no se usaba)

const fruits = ['peach', 'pineapple', 'strawberry']

const promesas = fruits.map(v=>getFruta(v));
const forFrutas= async () => {
  for await (const fruta of fruits){
  console.log(fruta)
  }
}

o se pueden ejecutar ciertas partes del programa a que esperen la resolución de una promesa sin bloquear así:

const inspection = async () ={
  if(await getFruit('peach') === '🍑'){
    console.log('tiene forma de pompis')
  }
}

Deja una respuesta

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Salir /  Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Salir /  Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Salir /  Cambiar )

Conectando a %s