Alex Padilla
Luis Fernando Morla Mora
Sergio Arturo Enriquez Nava
¿Cómo manejar asincronismo en servicios con Node.js?
Trabajar con servicios en Node.js implica, casi siempre, tratar con asincronismo, especialmente cuando nos conectamos a fuentes de datos externas como API de terceros, bases de datos como MongoDB o PostgreSQL, entre otros. Esto crea un "delay" debido a las conexiones. Aprender a manejar este asincronismo es crucial para evitar bloqueos en nuestras aplicaciones.
¿Qué rol juegan async y await?
Node.js se destaca por su capacidad de ejecutar operaciones de manera asíncrona, y la combinación de async y await simplifica mucho este proceso. Cualquier método que tenga la firma async se considera una promesa, lo que significa que se ejecuta de manera asíncrona.
Para comenzar a trabajar con asincronismo:
Definir métodos como asíncronos: Añade la palabra clave async antes de la declaración de funciones o métodos, aunque actualmente no implementen await. Esto las prepara para futuras conexiones con servicios externos asíncronos.
async function nombreDelMetodo() { // lógica }
Uso de await: Utiliza await cuando llamas a una función que devuelve una promesa. Esto pausará la ejecución de la función hasta que la promesa se resuelva.
async function obtenerDatos() { const resultado = await fetchDatosExterno(); // Continua con el procesamiento }
¿Cómo se emula la asincronía?
Aunque trabajamos en memoria y todo parece ocurrir de forma instantánea, podemos emular un comportamiento asíncrono para prepararnos. Por ejemplo, mediante la simulación de un retraso con setTimeout y las promesas:
async function obtenerProductos() { return new Promise((resolve, reject) => { setTimeout(() => { resolve(this.products); }, 5000); // Simula una latencia de 5 segundos }); }
¿Cómo podemos manejar errores asíncronamente?
El uso de try...catch en funciones que usan async y await permite manejar errores de manera elegante. Cuando un error es lanzado dentro de un bloque try, el control pasa a la sección catch.
Por ejemplo, para un método que actualiza datos:
async function actualizarProducto(idProducto) { try { const producto = await buscarProducto(idProducto); // Lógica para actualización } catch (error) { return response.status(404).json({ message: 'Producto no encontrado', error }); } }
Ventajas del manejo asíncrono
try...catch para controlar flujos de error sin interrumpir la ejecución del código.El uso de async y await ayuda a gestionar las operaciones asíncronas de manera eficiente, permitiendo crear aplicaciones de Node.js más rápidas, escalables y manejables. ¡Imprescindible para cualquier desarrollador en el ecosistema de Node.js! Sigue explorando y aplicando estos conceptos en tus proyectos.
Alex Padilla
Luis Fernando Morla Mora
Sergio Arturo Enriquez Nava
Aldo Said Salas Rodríguez
Jose Montoya
Maritza Pacheco Cuno
Enrique Ruvalcaba
Reinaldo Mendoza
Jose Jimenez
Nicolas Molina
ProfesorMax Andy Diaz Neyra
Jose Jean Piere Rodriguez Mejia
Luisana Indriago
Leonardo Balbastro
Jose Ever Muñoz Muñoz
Daniel Carmona
Sergio Andrés Bolaños Penagos
marlon mosquera rivera
Jose Ever Muñoz Muñoz
JOSE GABRIEL CASTILLO MOSQUERA
Juan Camilo Lentino Villalba
Jherom Chacon
Jhonatan Sanchez
Oscar Adrián Martínez Cavazos
Oscar Adrián Martínez Cavazos
Darvin Emil Rodriguez Nuñez
Enzo Retamozo
Luis Olivares
Mauricio Ruiz
Nicolas Martinez
Harold German Granados Guerrero
Sería una buena opción implementar el patrón singleton
Products Service
class ProductsService { static _productsServiceInstance = null; static getInstance() { if (ProductsService._productsServiceInstance === null) { ProductsService._productsServiceInstance = new ProductsService(); } return ProductsService._productsServiceInstance; }
Products.router
const express = require('express'); const ProductsService = require('../services/Products.service'); const router = express.Router(); //* GET all products router.get('/', getAll); //* GET product by ID router.get('/:id', getOne); //! ADD new product router.post('/', create); //* UPDATE partial product router.patch('/:id', updateProduct); //! DELETE product router.delete('/:id', deleteProduct); //* Internal Functions async function getAll(req, res) { const productsService = await ProductsService.getInstance(); const products = await productsService.find(); res.json(products); } async function getOne(req, res) { const productsService = await ProductsService.getInstance(); const { id } = req.params; const product = await productsService.findOne(id); res.json(product); } async function create(req, res) { const productsService = await ProductsService.getInstance(); const product = await productsService.create(req.body); res.json(product); } async function updateProduct(req, res) { const productsService = await ProductsService.getInstance(); const { id } = req.params; const data = req.body const answer = await productsService.update(id, data); res.json(answer); } async function deleteProduct(req, res) { const productsService = await ProductsService.getInstance(); const { id } = req.params; const answer = await productsService.delete(id); res.json(answer); } module.exports = router;
y que es el singleton?
Hola Luis Fernando, aquí encontré algo de información tampoco se que es pero pues aquí lo explican incluso dan un ejemplo. Espero te sirva. =)
Para todo aquel que se preguntó (igual que yo) de donde saco el .message en el minuto 6:16, pues es una propiedad del objeto Error.
Pueden leer más sobre esta propiedad en la documentación oficial de JavaScript.
Async nos permite usar el keyword await dentro de esa funcion. Await ejecuta el contenido de una funcion de forma sincrona,
gracias amigo
No se ejecuta de forma síncrona, sólo pareciese que el código se ejecuta de forma secuencial pero en realidad la ejecución sigue siendo asíncrona. Node.js es asíncrono a menos que explícitamente se indique la ejecución de algo de forma síncrona. Básicamente async await ayuda a tener un código más legible, evitando el promise chaining o callback hell.
Esta tremendo este curso, el profesor explica cada punto y coma, ya había visto estos patrones pero no llegaba a entenderlos del todo ni tampoco su valor agregado, ahora, por fin lo veo
Derepente siento que no es difícil programar, lo difícil es dominar los términos para cada aplicación.
Sera asi?
Wow es una gran reflexión cada persona tiene un proceso de aprendizaje diferente algunos les cuenta comprender conceptos a otros la practica, algoritmia etc. Lo importante es seguir.
Para aquellos que no conozcan o dominen el tema de asincronismo con async wait les recomiendo el curso de asincronismo con JS de platzi, muy bueno. Tambien les enseña callbacks y promises
Puedes compartir el link porfa
Hola Jose Jean, este es el enlace https://platzi.com/cursos/asincronismo-js/
Una mencion que me acaba de ocurrir: en la declaracion de la promesa los parametros estrictos son (resolve, reject) ya que si le pones alias te sale el siguiente error:
(node:10564) UnhandledPromiseRejectionWarning: TypeError: Expected a string, got object
por lo cual estrictamente es necesario que se declare de la siguiente manera:
async find(){ return new Promise((resolve,reject)=>{ setTimeout(() => { resolve(this.products); }, 5000); }) }
muy buena nota a tener en cuenta, no lo sabia
Excelente al cachar los errores
Para el método POST yo utilice como código de estado al momento de fallar un 400 que es un de que se hizo una mala respuesta, lo puse en el ejemplo de si faltaba algún dato por rellenar. Para encontrar el error en el servicio use un
async create({ product_name, price, image}){ if(product_name || price || image === undefined){ throw new Error('There is something missing'); } }
no de podria simplemente poner el async awai solo a las funciones de los router y no ponerle async a los metodos de las class
Hola Marlon.
Los metodos de la clase deben llevar async porque desde esos metodos es que se van a hacer consultas hacia la base de datos y el proceso de consultar la informacion puede demorarse, por tanto debe tener async y de hecho la linea que ejecuta la consulta debe llevar await.
Por el momento no es necesario pues los datos que consultamos estan en memoria, no estamos consultando una base de datos ni tampoco una API y los metodos funcionaran igual tanto si le pones async/await como si no.
Async / Await nos ayudo a simplificar el tema de promesas, ya que asi las podemos hacer mas amigables, este curso me ha gustado mucho he aprendido bastante, para ponerlo en practica en la fase 3 del js challenge
yo implemente promesas en todos los métodos de los servicios, y para el manejo de errores, no los lanzo con throw error, utilizo el reject routes/products.router.js:
const express = require('express') const router = express.Router() const productServices = require('../services/product.services') const services = new productServices() router.get('/', async (req,res)=> { try { const products = await services.find() res.json(products) } catch(e){ res.json({ message: `${e}` }) } }) router.get('/:id', async (req,res)=> { try { const { id } = req.params const findOne = await services.findOne(id) res.json(findOne) } catch(e){ res.json({ message: `${e}` }) } }) router.post('/', async (req,res)=> { try { const body = req.body const newProduct = await services.insert(body) res.status(201).json({ message: 'created successfully', product: newProduct }) } catch(e){ res.status(404).json({ message: `${e}` }) } }) router.put('/:id', async (req,res)=> { try { const { id } = req.params const body = req.body const newProduct = await services.update(id, body) res.json({ message: "update successfully", product: newProduct }) } catch(e){ res.json({ message: `${e}` }) } }) router.patch('/:id', async (req,res)=> { try { const { id } = req.params const body = req.body const newProduct = await services.updateParcial(id,body) res.json({ message: "update successfully", product: newProduct }) } catch(e){ res.json({ message: `${e}` }) } }) router.delete('/:id', async (req,res)=> { try { const { id } = req.params const productToDelete = await services.delete(id) res.json({ message: `delete product with id ${id} successfully`, product: productToDelete }) } catch(e){ res.json({ message: `${e}` }) } }) module.exports = router
services/products.services.js:
const { faker } = require('@faker-js/faker') class ProductsServices { constructor(){ this.products = [] this.generate() } generate(){ for (let i = 0; i < 4; i++){ this.products.push({ id: faker.datatype.uuid(), name: faker.commerce.productName(), price: faker.commerce.price(), image: faker.image.imageUrl() }) } } async insert ({ name, price }){ return new Promise((res,rej)=> { if (!name || !price){ rej('invalid params') return } const newProduct = { id: faker.datatype.uuid(), name, price, image: faker.image.imageUrl() } this.products.push(newProduct) res(newProduct) }) } async find(){ return this.products } async findOne(id){ return new Promise((res,rej)=> { const find = this.products.find(el => el.id == id) if (find){ res(find) return } rej('product not found') }) } async updateParcial(id, data){ return new Promise((res,rej)=> { const index = this.products.findIndex(el => el.id === id) if (index === -1){ rej('product not found') return } this.products[index] = { ...this.products[index], ...data } res(this.products[index]) }) } async update (id,data){ return new Promise((res,rej)=> { const index = this.products.findIndex(el => el.id === id) if (index === -1){ rej('product not found') return } const { name, price } = data if (!name || !price){ rej('invalid params') } this.products[index] = { id: this.products[index].id, image: faker.image.imageUrl(), name, price } res(this.products[index]) }) } async delete(id){ return new Promise((res,rej)=> { const find = this.products.find(el => el.id === id) const filter = this.products.filter(el => el.id != id) if (find){ this.products = filter res(find) return } rej('product not found') }) } } module.exports = ProductsServices
Mis servicios asincronos:
router.patch('/:id', async (req, res) => { try { const {id} = req.params; const body = req.body; const product = await service.update(id, body); res.json(product); } catch (error) { //Manejamos el error con asiync await res.status(404).json({ message: error.message }); } });
Hola, tengo un problema que me ocurrio mientras completaba los otros services y routers, lo que pasa es que me ejecuta todo bien hasta que intento hacer un get o lo que sea y me tira este error. Ya intente descargar el código del curso para que este tal cual en otra carpeta distinta y volvi a instalar los paquetes pero no funcionó :'(.
TypeError: Cannot read properties of null (reading '1') at firstchar (C:\Users\adria\Desktop\Curso\node_modules\body-parser\lib\types\json.js:176:37) at parse (C:\Users\adria\Desktop\Curso\node_modules\body-parser\lib\types\json.js:79:19) at C:\Users\adria\Desktop\Curso\node_modules\body-parser\lib\read.js:121:18 at invokeCallback (C:\Users\adria\Desktop\Curso\node_modules\raw-body\index.js:224:16) at done (C:\Users\adria\Desktop\Curso\node_modules\raw-body\index.js:213:7) at IncomingMessage.onEnd (C:\Users\adria\Desktop\Curso\node_modules\raw-body\index.js:273:7) at IncomingMessage.emit (node:events:402:35) at endReadableNT (node:internal/streams/readable:1343:12) at done (C:\Users\adria\Desktop\Curso\node_modules\raw-body\index.js:213:7) at IncomingMessage.onEnd (C:\Users\adria\Desktop\Curso\node_modules\raw-body\index.js:273:7) at IncomingMessage.emit (node:events:402:35) at endReadableNT (node:internal/streams/readable:1343:12) at processTicksAndRejections (node:internal/process/task_queues:83:21)
Hola a todos, ya encontré la solución. Para los que les de este error solo reinicien su postman XD. "A veces el problema no es uno, si no los demás". Me da gran alivio saber que no era un problema con mi código.
Excelente curso, tenia conocimiento de back-end en nodejs pero nunca habia tenido ni las buenas practicas y habian cosas que no sabia que aprendi aqui.
Si quieren pueden crear una función para no repetir cod de espera
const delay = (ms = 3000) => new Promise(res => { setTimeOut(res, ms) }) ... async findOne(id) { await delay(3000) return this.products.find(prod => prod.id === id) }
Recuerden que en promesas no se puede utilizar throw error, en su defecto usar reject.
Utilizando la sintaxis async/await si es posible el uso del throw, lo cual "rechazará" la promesa (si el método está marcado con async).
Si utilizas la sintaxis "de promesas", en efecto no se puede y deberás retornar una promesa, e invocar la funcion reject en caso de error; tal vez algo como:
function processSomething() { return new Promise((resolve, reject) => { if (somethingWrongHappens) { reject('a very useful error description'); } ... }); }
como se ajusta el status code en el router si tengo varias validaciones y no solo un 404
Hola no se si alguien mas le pasa o solo yo.
En el tiempo 3:05 al momento de cambiar el metodo find() agregando la promesa y el intervalo de tiempo.
Cuando busco los productos son aparece en el imsonia como resultado solo {} y no la lista.
Alguien sabe por que y a que se debe y como solucionarlo?