hand Inicio
hand JSBloqs
hand GutenBloqs
Qu茅?
noched铆a

DESARROLLO WEB con
REACT y WORDPRESS

Ap煤ntate a la newsletter (escribo algo de tanto en cuanto)

Combinando promesas (await async) con loops en JavaScript

400 palabras
2 minutos
July 3, 2020
blogjavascript

Los loops `forEach` y `map` no funcionan como los cl谩sicos `for`, con lo que su uso con `await` y `async` se complica

  1. Promesas con await y async
  2. Y cuando estamos en un loop qu茅, funciona igual?
  3. Loop con reduce

Promesas con await y async

Las promesas en JavaScript fueron un gran qu茅, pero eran y son un peque帽o caos

El objetivo? Conseguir una manera de no bloquear la ejecuci贸n del c贸digo mientras una instrucci贸n a煤n no se hab铆a completado

  • Por ejemplo, leer datos de una fuente externa

C贸mo funcionan las promesas? Nos devuelven una promise y una manera de saber cu谩ndo esa promesa se resuelve

El concepto es perfecto, pero en c贸digo la implementaci贸n no fue brillante

Entonces vino el async y el await, una manera de expresar promesas que s铆 es brillante

Por ejemplo, vamos a suponer que aqu铆 la funci贸n fetchMyData nos devuelve una promesa

js
const fetching = () => {
const data = fetchMyData()
console.log(data) // me sale "pending"
}
fetching()

En este c贸digo necesitamos esperar a que data exista (es decir a que la promesa que nos devuelve fetchMyData se resuelva)

Pues tan sencillo como esto

js
const fetching = async () => {
const data = await fetchMyData()
console.log(data) // data es lo que sea que fuere data
}
fetching()

Y cuando estamos en un loop qu茅, funciona igual?

Para los loops "cl谩sicos" la respuesta era s铆, funcionan igual

Por ejemplo con un for..of

js
// funci贸n que implementa un wait con promesas
const wait = ms => new Promise((r, j) => setTimeout(r, ms))
// funci贸n que emula un proceso as铆ncrono
const fetchUrl = async url => {
await wait(1000)
return 'fantastic ' + url
}
// aqu铆 vamos a hacer ver que pedimos 3 webs, y lo hacemos con un loop for .. of
const fetching = async () => {
const urls = ['https://www.site1.com', 'https://www.site2.com', 'https://www.site3.com']
for (const url of urls) {
const data = await fetchUrl(url)
console.log(data)
}
}
fetching()
Ver en CodeSandBox

En este loop las cosas funcionan como queremos, un console.log a cada segundo, fabuloso

Pero y si queremos un loop menos declarativo y m谩s funcional?

Por loops funcionales tenemos el forEach y el map, y estos loops no funcionan como esperar铆amos

La diferencia entre un forEach y un map es que el segundo nos devuelve un valor, y el primero no

Vamos a verlo con un forEach, y ver谩s que he puesto el async dentro de la funci贸n forEach, eso ya te da una pista de lo que est谩 ocurriendo

js
const wait = ms => new Promise((r, j) => setTimeout(r, ms))
const fetchUrl = async url => {
await wait(1000)
return 'fantastic ' + url
}
const fetching = () => {
const urls = ['https://www.site1.com', 'https://www.site2.com', 'https://www.site3.com']
urls.forEach(async url => {
const data = await fetchUrl(url)
console.log(data)
})
}
fetching()
Ver en CodeSandBox

Funciona como esperar铆amos?

No

Tenemos los console.log que aparecen al instante y no a cada segundo

Por qu茅?

Porque todos los console.log se ejecutan a la vez

Por qu茅?

forEach y map ejecutan una funci贸n que recibe como argumento el elemento del array

js
Array.forEach((argumentos) => {})

Bien, pues el problema es que esas funciones las ejecuta en paralelo, no podemos decirle que se espere

Y adem谩s, tampoco podemos bloquear el c贸digo de todo el forEach completo

Lo puedes comprobar en este ejemplo

js
const wait = ms => new Promise((r, j) => setTimeout(r, ms))
const fetchUrl = async url => {
await wait(5000) // ahora son 5 segundos
return 'fantastic ' + url
}
const fetching = () => {
const urls = ['https://www.site1.com', 'https://www.site2.com', 'https://www.site3.com']
urls.forEach(async url => {
const data = await fetchUrl(url)
console.log(data)
})
console.log('hemos acabado') // sale al principio!
}
fetching()
Ver en CodeSandBox

Aqu铆 el mensaje hemos acabado nos aparece inmediatamente, y a los 5 segundos nos aparecen los tres fantastic ... a la vez

Es decir, aqu铆 no se bloquea nada

Una opci贸n podr铆a ser ponerle un await delante del forEach, pero tampoco funciona puesto que el forEach no devuelve ninguna promesa

Si en lugar de un forEach utilizamos map, 茅ste nos devuelve una promesa por cada elemento, y podr铆amos hacer lo siguiente

js
const fetching = async () => {
const urls = ['https://www.site1.com', 'https://www.site2.com', 'https://www.site3.com']
const promesas = urls.map(async url => {
const data = await fetchUrl(url)
})
await Promise.all(promesas)
console.log('hemos acabado')
}
fetching()

As铆 conseguimos bloquear el c贸digo hasta que termine todo el map, pero no podemos bloquear cada iteraci贸n del loop (ocurren en paralelo)

C贸mo podemos hacerlo?

Con reduce (tienes una entrada aqu铆)

Loop con reduce

js
const wait = ms => new Promise((r, j) => setTimeout(r, ms))
const fetchUrl = async url => {
await wait(1000)
return 'fantastic ' + url
}
const fetching = async () => {
const urls = ['https://www.site1.com', 'https://www.site2.com', 'https://www.site3.com']
await urls.reduce(async (previousPromise, url) => {
await previousPromise
const data = await fetchUrl(url)
console.log(data)
return Promise.resolve()
}, Promise.resolve())
console.log('hemos acabado')
}
fetching()
Ver en CodeSandBox

reduce nos permite incrustar una funci贸n en cada ciclo del loop, por lo que podemos implementar una promesa y esperar a que se resuelva, y as铆 bloquear el c贸digo como lo hac铆amos en el for..of anterior

Y ya lo tenemos

Dicho esto, mejor con reduce o mejor con for..of?

Depende de tu caso

  • Bien podr铆as definir una funci贸n global con reduce, refactorizarla y reutilizarla y as铆 poder disfrutar del paradigma funcional

  • O podr铆as utilizar for..of y quedarte tan contento, no hay nada malo en mezclar paradigmas

馃檵鈥嶁檪锔

Qu茅 tal la entrada?

馃憣 Bien 馃檶馃檶
馃憤 Bien, pero algunas cosas podr铆an explicarse mejor 馃槵
馃し鈥嶁檪锔 Da por sentadas demasiadas cosas 馃槖
馃し鈥嶁檪锔 A ver, hay poca chicha 馃槵
馃し鈥嶁檪锔 Los ejemplos no son muy claros 馃檱鈥嶁檪锔
馃し鈥嶁檪锔 No se entiende, est谩 mal escrito 馃憥
鉁嶏笍 Hay errores, rev铆salo en cuanto puedas 馃檹
Enviar Feedback 鉁嶏笍
El texto est谩 en blanco!
Gracias por enviarme tu opini贸n
馃憤

Si quieres explorar m谩s cursos y m谩s entradas en el blog, los tienes todos en la p谩gina principal, y si el contenido te ha ayudado si lo compartes me ayudas a dar difusi贸n 馃憤

Privacidad
by kuworking.com
[ 2020 >> kuworking ]