Ejemplo de uso async await y explicacion de promesas

Coloquialmente las promesas en el contexto del lenguaje de programacion javascript las podemos definir como un estado de los datos de los cuales queremos obtener el resultado bien sea de rechazado o exitoso . Esto se creo con el fin de controlar el exito o la falla de operaciones que son consideradas operaciones con carga computacional en donde es necesario concretar el resultado de la misma sin que afecte la continuidad del programa. Con esto ultimo queremos enfatizar de que el programa puede estar esperando para concretar una operacion de E/S o BD y asincronamente seguir el curso natural del programa, sin tener que esperar por la finalizacion del proceso, ejemplo (con fines teorico-explicativos): javascript const resultado = procesarArchivo('texto.info')//proceso largo con archivo de mas de diez mil lineas resultado.then( exitoso => { console.log('proceso exitoso') }) .catch( fallo => { console.error('proceso fallido') }) console.log('continua el proceso') console.log('termina el proceso') Analicemos a fondo el ejemplo, en un lenguale procedural tales como c++, el curso del programa y la salida que arrojaria seria la siguiente: bash proceso exitoso (si y solo si es exitoso) proceso fallido (si y solo si es fallido) continua el proceso termina el proceso Pero en javascript con la filosofia de programacion asincrona realmente el orden de ejecucion seria este: bash continua el proceso termina el proceso proceso exitoso (si y solo si es exitoso) proceso fallido (si y solo si es fallido) Por ello el control de las promesas con then y catch solo debe usarse en casos en donde se requiera procesamiento asincronico tales como borrado de un archivo en donde el borrado del mismo no afecta el curso natural del programa, o en una tarea de limpieza de datos en BD, en cual no es relevante para el procesamiento general de la secuencia logica que se este aplicando al programa en cuestion. En MDN Mozilla, hay un poco mas sobre promesas Articulo promesas Hasta ahora hemos entendido en terminos generales algunos beneficios del uso de promesas y como su uso influye en nuestros programas o como cambian el curso de ejecucion, pero en el contexto real. Siempre necesitaremos ejecucion asincrona ? Que pasa si necesitamos hacer dos operaciones en donde la operacion 2 dependa de la operacion 1 Con el punto dos se suele dar una solucion muy tentadora y rapida como esta: javascript const resultado1 = operacion1(params) resultado1.then( exitoso1 => { const resultado2 = operacion2(exitoso1) resultado2.then( exitoso2 => { console.log(exitoso2) }) .catch(fallo2 => { console.error('fallo 2') } ) }) .catch( fallo1 => { console.error('fallo 1') }) Bien, por ahora tenemos la solucion a 2 operaciones dependientes entre si en donde una vez finalizado con exito una operacion, su resultado es lo que utilizara la operacion 2 para su ejecucion, ahora bien en terminos de buenas practicas a lo clean code : Se ve bien ? Tener anidados dos sentencias then te resulta atractivo al menos para leer ?

  • Que pasa si tienes 4 operaciones dependientes? las anidarias todas ?, crees que esta bien ? Con este ultimo punto nos referimos a una situacion que le han denominado callback hell o el infierno del callback , como es de suponer las funciones anonimas que contienen las sentencias then y catch son funciones callback el abuso de estas puede resultar en una legibilidad de codigo muy pobre e incluso no poder escalar tanto como el negocio necesite. A continuacion un ejemplo de callback hell habitual: javascript const unaFuncion = () => { setTimeout(() => { console.log('1. cosa') setTimeout(() => { console.log('2. cosa') setTimeout(() => { console.log('3. cosa') setTimeout(() => { console.log('4. cosa') }, 2000) }, 2000) }, 2000) }, 2000) } Todo un dolor de cabeza, verdad ? Nueva sintaxis Durante un largo tiempo de discusion en el comite del estadar ecma-script se adopto una nueva sintaxis la cual permite mantener un mejor flujo en las operaciones dependientes y una mejora de la legibilidad muy notable, por ello desde la version ES7 se siguieron los siguientes pasos importantes que consideramos en esta organizacion para que usted adopte como via de estandarizacion de los modulos que aca se desarrollan: Para declarar variables en el antiguo javascript se solia usar: var miVariable = 1 , lo cual puede tener un efecto adverso ya que las variables declaradas con var son de ambito global y puede ser sobreescrito en cualquier segmento del programa produciendo una mutacion de estados, por lo tanto en el ES6 se decidio usar la declaracion de variables por ambito y por alcance, ejemplo: let b = 1 //solo sera local a su ambito mas inmediato const c = 2 //si se intenta sobrescribir, el interprete arrojara un error de que una variable const o constante no puede ser reasignada En la sintaxis vieja para declarar una funcion se solia hacer esto: javascript function miFuncion1(param1, param2){ console.log(1) return 1 } //o para funciones anonimas .then(function(param){ console.log(2) return 2 }) Se cambio de la siguiente forma, la cual denominaron arrow functions o funciones de flecha por su sintaxis muy parecida a flechas: javascript //Es buena practica usar const en funciones ya que la misma no debe ser reasignada const miFuncion1 = (param1, param2) => { console.log(1) return 1 } //o para funciones anonimas .then((param) => { console.log(2) return 2 }) Tal como se muestra en el codigo de ejemplo anterior se podrian usar las funciones, pero si la funcion ocupa solo una linea y ademas solo un parametro se puede usar de la siguiente forma: `javascript const miFuncion2 = param => param + 1 //Se puede omitir el return y llaves si solo es 1 linea

//o para funciones anonimas .then( param => param + 1) El uso de ; al final de cada instruccion es opcional, sin embargo aqui en stacksavings hemos decidido no colocarlo por motivos de simplicidad, prueba de ello han sido los ejemplos anteriores. Como Mitigar el callback hell a traves de async/await + bluebird Volviendo al tema de las promesas y el infierno del callback, en stacksavings hemos encontrado la forma de no caer en callback hell y asi mismo controlar el flujo de procesos computacionales extensos como puede ser obtener y guardar los currencies actuales sin saturar las operaciones por medio de BD. Para ello usamos las herramientas async/await provistas en la version ES6 de Javascript el cual se debe cumplir lo siguiente: Solo debe ser usado cuando una operacion retorna una promesa No se puede usar un await si no se tiene un async previo Para nuestro primer ejemplo, consideremos una funcion que lee una informacion de un archivo y de una BD para luego retornarlo, ejemplo de uso:javascript const procesar = async () => { let infoArchivo, infoBD, errorArchivo, errorBD errorArchivo, infoArchivo = await obtenerInfoArchivo('file.txt') errorArchivo !== null ? retornarError(errorArchivo) : '' errorBD, infoBD = await ObtenerInfoBD('Tabla1') errorBD !== null ? retornarError(errorBD) : '' return concatenarInfo(infoArchivo, infoBD) } const resultado = procesar() resultado .then(res => { console.log(res) }) .catch(err => { console.log(err) }) Es un caso ideal para cuando se necesite procesar una informacion y retornar de donde se llamo a procesar, de lo contrario si solo se necesita procesar informacion y no retornar, no hace falta utilizar then . Aca en stacksavings comprendemos que puede resultar un poco confuso a la hora de abordar un problema de esta naturaleza con estas herramientas, Ahora bien, observe el siguiente ejemplo:javascript const procesar2 = async () => { let infoArchivo, infoBD, errorArchivo, errorBD errorArchivo, infoArchivo = await obtenerInfoArchivo('file.txt') errorArchivo !== null ? retornarError(errorArchivo) : '' errorBD, infoBD = await ObtenerInfoBD('Tabla1') errorBD !== null ? retornarError(errorBD) : '' guardarInfoEnBD(infoArchivo, infoBD) } procesar2() Comparemos los 2 ejemplos, en procesar() necesitabamos obtener el resultado de 2 operaciones por ende usamos el then ya que al usar async retornaria una promesa, en cambio en procesar2() necesitabamos solo procesar una informacion sin tener que retornar ningun resultado. Hasta ahora hemos comprendido la importancia de usar async/await para el control del flujo de operaciones computacionales relacionados con las promesas. Nos complace enormemente que hasta aqui se pueda haber entendido todo con claridad. A lo largo de nuestra carrera profesional nos hemos topado con cualquier cantidad de problemas en donde se requiere un esquema general de soluciones, por lo tanto el alcance de solucion que le hemos presentado anteriormente con async/await solo cumple hasta un espacio de problemas reducidos al numero de llamadas a promesas de forma estatica, es decir, a un numero de llamadas determinado. Como en el ejemplo anterior solo se hicieron 2 llamadas a funciones que retornaban promesas, puede que en un futuro necesite 3,4 o mas, pero suelen suceder casos en los que no sabe cuantas promesas tiene que procesar, por ejemplo, se necesita procesar archivos presentes en un directorio pero no se sabe a priori cuantos archivos son, el numero de archivos presentes en el directorio es desconocido, Que se hace en estos casos ? , pues en StackSavings hemos encontrado la manera de hacer funcionar la libreria BlueBird con async/await para resolver este tipo de problemas. Fusion con Bluebird BlueBird Es una libreria extensa para el control y manejo de promesas. Brinda una flexibilidad en cuanto a la manipulacion de las mismas. Aca en stackSavings hemos encontrado la forma de gestionar un numero indetermidado de promesas de modo que se pueda controlar el flujo del proceso computacionalmente tal como el ejemplo que citamos previamente del procesamiento de archivos presentes en un directorio. Supongamos que leimos un directorio con el modulo nativo de node filesystem (fs) y el mismo nos devuelve una coleccion de todos los nombres presentes en el directorio, por ejemplo: let archivos = ['file1.txt', 'file2.txt'] para procesarlos usaremos una funcion "ficticia" que se llama procesarArchivo(archivo) la cual devuelve una promesa de exito o rechazo, para estos casos solemos usar la sentencia de bluebird each en donde iteraremos la coleccion de archivos para su procesamiento ordenado de las promesas, aca un ejemplo de codigo:javascript let PromiseBluebird = require('bluebird') const procesamientoDeArchivos = async (archivos) => { await PromiseBluebird.each(archivos, async(archivo) => { await procesarArchivo(archivo) }).catch(err => { logger.error('error al procesar el archivo: ' + err) }) } ` Ahora analicemos un poco el codigo en cuestion, para cada posicion del la coleccion que seria un nombre de archivo, se mandara a procesar el archivo, y luego pasara al siguiente, se comporta similarmente a un ciclo, solo que este es un ciclo que maneja promesas. Escrito originalmente por: Hendrix Roa