Async/Await llega a Firefox

21 marzo, 2017 3:56 por

Esta es una traducción del artículo original publicado en el blog de Mozilla Hacks.

Las nuevas palabras clave async y await — las cuales hacen el código asíncrono más conciso, obvio y mantenible — han llegado en Firefox 52.

JavaScript debe su excelente rendimiento de hilo único y responsividad en la web a su generalizado diseño asíncrono. Desafortunadamente, ese mismo diseño resulta en un aumento del “infierno del callback”, donde llamadas secuenciales a funciones asíncronas requieren anidación profunda, código difícil de manejar, como vemos en este ejemplo inventado ligeramente usando la librería localforage:

function foo(callback) {
  localforage.setItem('x',  Math.random(), function(err) {
    if (err) {
      console.error("Algo salió mal:", err);
    } else {
      localforage.getItem('x', function(err, value) {
        if (err) {
          console.error("Algo salió mal:", err);
        } else {
          console.log("El número aleatorio es:", value);
        }

        if (callback) {
          callback();
        }
      });
    }
  });
}

foo(function() { console.log("Listo!"); });

Si pasaste por alto este código, o no entendiste inmediatamente lo que hizo, ese es el problema.

ES2015 comenzó a direccionar este reto mediante estandarización en Promesas para funciones asíncronas encadenadas. Desde su introducción, las Promesas se han convertido en una parte integral de los nuevos estándares web, incluyendo fetch y service workers. Ellas hacen posible reescribir el ejemplo anterior como:

function foo() {
  return localforage.setItem('x', Math.random())
         .then(() => localforage.getItem('x'))
         .then((value) => console.log("El número aleatorio es:", value))
         .catch((err) => console.error("Algo salió mal:", err));
}

foo().then(() => console.log("Listo!"));

Gracias a las Promesas, el código no se anida de manera más profunda en cada llamada sucesiva, y todo el manejo de errores puede ser consolidado en un único caso al final de la cadena.

Date cuenta que en el ejemplo de arriba, foo() devuelve inmediatamente, antes de que localforage haga su trabajo. Porque foo() en sí mismo devuelve una Promesa, llamadas futuras pueden ser planificadas para después de que se complete con el método .then().

Semánticamente, el ejemplo de arriba es mucho más directo, pero sintácticamente, hay todavía mucho que leer y entender. Las nuevas palabras clave async y await son azúcar sintáctico encima de las Promesas para ayudar a hacer las Promesas más manejables:

async function foo() {
  try {
    await localforage.setItem('x', Math.random());
    let value = await localforage.getItem('x');
    console.log("El número aleatorio es:", value);
  } catch (err) {
    console.error("Algo salió mal:", err);
  }
}

foo().then(() => console.log("Listo!"));

El código de arriba es idéntico funcionalmente al ejemplo anterior, pero es mucho más fácil de comprender y mantener, dado que el cuerpo de la función ahora se parece a una función común síncrona.

Las funciones marcadas con async siempre devuelven Promesas, y asi llamadas a .then() funcionan en su valor de retorno para programar callbacks. Las expresiones prefijadas con await efectivamente pausan las funciones hasta que la expresión se resuelve. Si una expresión esperada con await se encuentra con un error, entonces la ejecución se pasa al bloque catch. Si no se captura, la Promesa devuelta se asienta en un estado rechazado.

Similarmente, en lugar de manejar errores dentro de funciones async, es posible usar métodos .catch() normales en el valor de retorno en su lugar:

async function foo() {
    await localforage.setItem('x', Math.random());
    let value = await localforage.getItem('x');
    console.log("El número aleatorio es:", value);
}

foo().catch(err => console.error("Algo salió mal:", err))
     .then(() => console.log("Listo!"));

Para un ejemplo más prácticos, considera una función que puedas escribir para cancelar la suscripción a un usuario de notificaciones web push:

function unsubscribe() {
  return navigator.serviceWorker.ready
         .then(reg => reg.pushManager.getSubscription())
         .then(subscription => subscription.unsubscribe())
         .then(success => {
           if (!success) {
             throw "No se pudo cancelar la suscripción";
           }
         });
}

Con async y await, se convierte en:

async function unsubscribe() {
  let reg = await navigator.serviceWorker.ready;
  let subscription = await reg.pushManager.getSubscription();
  let success = await subscription.unsubscribe();
  if (!success) {
    throw "No se pudo cancelar la suscripción";
  }
}

Ambas funciones son idénticas, pero el último ejemplo esconde las complejidades de Promesas, y vuelve código asíncrono en código que se lee (y se ejecuta) como código síncrono: desde arriba hacia abajo, esperando por cada línea de código de resolverse completamente antes de moverse a la próxima línea.

La compatibilidad nativa entre navegadores para las palabras clave async y await es todavía reciente, pero puedes usarlas hoy para la ayuda de un transpilador Javascript como Babel, el cual puede convertir async / await en funcionalidad equivalente en código con compatibilidad hacia atrás.

Para aprender más sobre las palabras clave async y await, o Promesas en general, echa un vistazo a los siguientes recursos:

Recuerda, async y await son solo utilidades para Promesas: tu puedes mezclar y combinar cualquier sintaxis, y todo lo que aprendas sobre Promesas aplica directamente a async y await.

Agradecimientos especiales a Jamund Ferguson por sugerir mejoras a los códigos de ejemplo en este post.

The following two tabs change content below.

Compartir artículo:

Empezar la discusión en foro.mozilla-hispano.org

cc-by-sa