Devolución de llamadas de JavaScript, promesas y funciones asíncronas Parte 2

Introducción

En la primera parte de este tutorial, aprendimos los principios de la programación asíncrona y el uso de devoluciones de llamada. Para revisar, la programación asíncrona nos permite escribir código que no es de bloqueo ejecutando tareas de bloqueo más adelante. Las funciones de devolución de llamada proporcionan una manera de sincronizar la ejecución de nuestro código. 

Sin embargo, anidar devoluciones de llamada repetidamente no es un buen patrón a seguir. Aquí vienen las promesas al rescate. Las promesas se han utilizado durante un tiempo en las bibliotecas de JavaScript, pero ahora puedes usarlas de forma nativa en tu código. Las funciones asíncronas mejoran las promesas al permitirnos escribir nuestras tareas una tras otra sin que tengamos que preocuparnos por el momento de su ejecución.

Contenido

  • Promesas
  • Funciones asincrónicas
  • revisión
  • Recursos

Promesas

Echemos un vistazo a lo que es una promesa conceptualmente. Imagina el escenario donde compras en línea y compras un par de zapatos. Cuando se retira, recibe un correo electrónico con un resumen de su compra.. 

Esta confirmación de pedido es como una promesa. La promesa es su garantía de que obtendrá algo más tarde de la compañía. Mientras su orden está pendiente, su vida no se detiene, por supuesto. Sigues realizando otras tareas como navegar por internet. Si su pedido se cumple, recibirá un correo electrónico con la información de envío. Es posible que su pedido sea rechazado. El artículo que ordenó puede estar agotado, o podría haber un problema con su método de pago. En este caso, recibiría un correo electrónico informándole del error..

En el código, una promesa es un objeto que garantiza que obtendremos un valor futuro para nuestra solicitud, ya sea que tenga éxito o no. Esta es la forma general para crear y usar una promesa:

función tarea1 () devolver nueva Promesa (función (resolver, rechazar) resolver (datos); rechazar (error););  task1 () .then (function (result) console.log (result);) .catch (function (error) console.log (error););

Para crear una promesa, crea una instancia de un objeto de promesa y escribe su código asíncrono dentro de la función de devolución de llamada de la promesa. Los datos que desea que se devuelvan de la promesa se pasan como un argumento a la resolver función, y su mensaje de error se pasa a la rechazar función. Encadenamos promesas usando el entonces método. Esto nos permite ejecutar las tareas de forma secuencial.. 

Si necesitamos pasar los resultados de una tarea a la siguiente tarea, lo devolvemos en el entonces método. Es posible que queramos encadenar promesas cuando estamos interesados ​​en transformar valores o necesitamos ejecutar nuestro código en un orden particular. Al final de la cadena, atrapamos nuestros errores. Si ocurre un error en cualquiera de nuestras tareas, las tareas restantes se omiten y el error se envía a nuestro bloque catch.

En la primera parte de este tutorial, utilizamos las devoluciones de llamada para abrir un archivo y recuperar una publicación y sus comentarios. Así es como se ve el código completo usando promesas:

index.js

const fs = require ('fs'); const path = require ('path'); const postsUrl = path.join (__ dirname, 'db / posts.json'); const commentsUrl = path.join (__ dirname, 'db / comments.json'); // devolver los datos de nuestra función de archivo loadCollection (url) return new Promise (función (resolver, rechazar) fs.readFile (url, 'utf8', función (error, datos) si (error) rechazar (' error '); else resolver (JSON.parse (data));););  // devolver un objeto por la función id getRecord (collection, id) return new Promise (function (resolver, rechazar) const data = collection.find (function (element) return element.id == id;); resolver (datos););  // devuelve una serie de comentarios para una función de publicación getCommentsByPost (comments, postId) return comments.filter (function (comment) return comment.postId == postId;);  // código de inicialización loadCollection (postsUrl) .then (function (posts) return getRecord (posts, "001");) .then (function (post) console.log (post); return loadCollection (commentsUrl); ) .then (función (comentarios) const postComments = getCommentsByPost (comentarios, "001"); console.log (postComments);) .catch (function (error) console.log (error););

La diferencia aquí es que nuestro método para abrir el archivo ahora está envuelto en un objeto de promesa. Y en lugar de anidar nuestras tareas con devoluciones de llamada, se encadenan con entonces

Como puede ver, no hemos eliminado la necesidad de devoluciones de llamada. Solo los estamos usando de manera diferente. Antes, anidábamos nuestras devoluciones de llamada para poder continuar con la ejecución de nuestro código en la siguiente tarea. 

Esto me recuerda cuando llamo al servicio de atención al cliente sobre un problema y, en lugar de que el agente resuelva mi problema, me transfiero a otra persona. Que alguien más pueda o no resolver la llamada, pero en lo que respecta al primer agente, es responsabilidad de alguien más.. 

Con las promesas, recuperaremos algo antes de pasar a la siguiente tarea. Si necesitamos llevar ese algo a la siguiente continuación de nuestro código, podemos usar un entonces declaración.

Tarea

Usando promesas, escriba un programa que abra un archivo de usuarios, obtenga la información del usuario y luego abra un archivo de publicaciones e imprima todas las publicaciones del usuario.

Funciones asincrónicas

Las promesas son una mejora en el diseño de nuestro programa, pero podemos hacerlo mejor. Sería muy conveniente si pudiéramos ejecutar nuestras tareas de forma síncrona de esta manera:

tarea 1(); tarea 2(); task3 ();

Bueno, podemos con el patrón async / await. Para hacer esto, comenzamos envolviendo nuestras tareas en una función asíncrona. Esta función devuelve una promesa. Luego implementamos el manejo de errores envolviendo nuestras tareas dentro de un trata de atraparlo declaración. 

Si la promesa se cumple, completará cualquier tarea que se encuentre dentro de nuestro tratar bloquear. Si es rechazado, el captura Se ejecutará el bloque. Añadiendo el esperar palabra clave antes de que cualquier tarea detenga nuestro programa hasta que la tarea se complete. 

Esta es la forma general para usar funciones asíncronas:

función asíncrica initTasks () try const a = await task1 (); const b = aguarda task2 (); const c = aguarda task3 (); // hacer algo con a, b, yc catch (error) // hacer algo con el objeto de error initTasks ();

Usando este patrón, podemos reescribir cómo ejecutamos nuestro código en nuestros archivos de ejemplo.

función asíncrica getPost () try const posts = await loadCollection (postsUrl); const post = await getRecord (posts, "001"); const comments = aguardan loadCollection (commentsUrl); const postComments = await getCommentsByPost (comments, post.id); console.log (post); console.log (postComments);  catch (error) console.log (error);  getPost ();

Me gusta estructurar nuestro código asíncrono con un trata de atraparlo declaración porque separa claramente el código de manejo de errores del código regular. Si alguno de los códigos en nuestro tratar bloque provoca un error, será manejado por el captura bloquear. Además, podemos agregar un finalmente bloque que ejecutará código independientemente de si nuestras tareas tienen éxito o fallan. 

Un ejemplo para usar este patrón es cuando tenemos un código de limpieza que necesitamos ejecutar. Este código no necesariamente tiene que estar contenido en un finalmente bloquear. Puede ser escrito después de la captura declaración, y se ejecutará. Las promesas no tienen esta sintaxis incorporada. Tendríamos que encadenar otra entonces declaración después de nuestra captura Declaración para lograr el mismo efecto..

Tarea

Usando async / await, escriba un programa que abra un archivo de usuarios, obtenga la información del usuario y luego abra un archivo de publicaciones e imprima la información del usuario y todas sus publicaciones..

revisión

Las devoluciones de llamada no son intrínsecamente malas. Pasar funciones a otras funciones es un patrón útil en JavaScript. Las devoluciones de llamada se convierten en un problema cuando las estamos utilizando para controlar el flujo de la lógica de nuestra aplicación. Dado que JavaScript es asíncrono, debemos cuidar cómo escribimos nuestro código porque las tareas no necesariamente terminan en el orden en que fueron escritas. Eso no es algo malo porque no queremos que ninguna tarea bloquee la continuación del programa.. 

Las promesas son un mejor patrón para escribir código asíncrono. No solo solucionan el desorden de devoluciones de llamadas anidadas, sino que también mantienen el control del resultado de una tarea dentro de la tarea. Pasar el control a otra tarea, ya sea que esa tarea sea nuestro propio código o una API de terceros, hace que nuestro código sea menos confiable. Por último, las funciones asíncronas nos permiten escribir nuestro código de forma síncrona, que es mucho más intuitivo y más fácil de entender..

Recursos

  • No sabes JS: Async & Performance Ch 3: Promesas
  • Promesas de JavaScript: una introducción
  • Funciones asíncronas: hacer promesas amigables
  • Async / Await de Patrick Triest