Loops de JavaScript, diferencias entre FOR versus FOREACH
El forEach
nos permite programar de modo declarativo (lo cual acostumbra a ser mucho mejor), pero ojo con el{' '}
await
y el async
, porque con el forEach no funcionan como esperaríamos
Prólogo
Iterar sobre objects
es una de las operaciones más útiles en JavaScript ya que muchas veces los objetos representan la manera más versátil que tenemos para organizar nuestros datos
El problema de utilizar loops
es que no es tan sencillo como parece, las opciones de las que disponemos son bastantes y vale la pena conocer las diferencias entre ellas
Aquí compararé el clásico for
con el forEach
, que pueden parecer lo mismo pero son entre distintos y muy distintos
El loop for con arrays y objects
El loop for
nos permite iterar en una secuencia de números
- En un
array
estos números pueden ser sus índices para luego tener acceso a sus valores - En un
object
necesitaremos transformar el objeto en array, y el código final resulta una pesadilla
En resumen, este loop ha quedado anticuado aunque es el que (normalmente) ofrece un mayor rendimiento
const myArray = [
'kuworking',
'Aprende desarrollo web en kuworking.com',
]
for (let i = 0; i < myArray.length; i++ ) // myArray.length es igual a 2
console.log(myArray[i])
// kuworking
// Aprende desarrollo web en kuworking.com
const myObject = {
name: 'kuworking',
description: 'Aprende desarrollo web en kuworking.com',
}
for (let i = 0; i < Object.entries(myObject).length; i++) {
console.log(Object.keys(myObject)[i])
console.log(myObject[Object.keys(myObject)[i]])
}
// name
// description
// kuworking
// Aprende desarrollo web en kuworking.com
Para iterar arrays y objetos tenemos otros loops más modernos, los tienes en la entrada Loops de JavaScript, diferencias entre FOR..IN versus FOR..OF
Pero aquí hablaremos del forEach
, un loop distinto a todos los demás
El loop forEach
Y por qué es distinto a los demás? Porque nos permite programar de un modo funcional o declarativo y no imperativo
Diferencias?
- Con el imperativo lo primero que lees es cómo lo haces, y luego ves el qué
- Con el funcional lo primero que lees es el qué, y luego ves el cómo
Es algo tan básico como que delante de una estructura for
con sus paréntesis y sus variables tendrás sí o sí que pararte y entretenerte si quieres saber qué pasa allí
Con un foreach
ya sabrás la variable y que hay un foreach, no te hará falta entretenerte para tener una idea básica de lo que ocurre
Y esto te permite
- Leer el código más rápido
- Escribir el código más rápido
Funciona así
const myObject = {
name: 'kuworking',
description: 'Aprende desarrollo web en kuworking.com',
}
Object.entries(myObject).forEach(el => console.log(el))
// ["name", "kuworking"]
// ["description", "Aprende desarrollo web en kuworking.com"]
Hasta aquí la diferencia conceptual
Pero hay una diferencia práctica que importa y mucho
Await y Async con el forEach
Este código funciona como esperamos (voy a utilizar un for..of
por comodidad)
const timeout = (method, ms) =>
new Promise(resolve =>
setTimeout(() => {
method()
resolve()
}, ms)
)
const myObject = {
name: 'kuworking',
description: 'Aprende desarrollo web en kuworking.com',
}
;(async () => {
for await (const [key, value] of Object.entries(myObject)) {
await timeout(() => console.log('key -> ' + key), 1000)
console.log('value: -> ' + value)
}
})()
// "key -> name"
// "value: -> kuworking"
// "key -> description"
// "value: -> Aprende desarrollo web en kuworking.com"
Es decir, esperamos 1 segundo, luego tenemos una impresión de la primera pareja de valores en orden, luego esperamos otro segundo, y entonces nos aparecen la segunda pareja de valores
Pero si queremos hacer lo mismo con forEach
vemos que no funciona
const timeout = (method, ms) =>
new Promise(resolve =>
setTimeout(() => {
method()
resolve()
}, ms)
)
const myObject = {
name: 'kuworking',
description: 'Aprende desarrollo web en kuworking.com',
}
Object.entries(myObject).forEach(async ([key, value]) => {
await timeout(() => console.log('key -> ' + key), 1000)
console.log('value: -> ' + value)
})
// "value: -> kuworking"
// "value: -> Aprende desarrollo web en kuworking.com"
// "key -> name"
// "key -> description"
Aquí nos siguen apareciendo por orden, pero nos aparecen las 2 parejas de valores al mismo tiempo, en lugar de esperarnos 2 segundos nos esperamos sólo 1 segundo
Haz la prueba y pon un valor de 10 segundos
- Con el
for of
esperarías 10 segundos, luego una pareja, luego 10 segundos más, luego la otra pareja - Con el
forEach
esperarás 10 segundos y verás las 2 parejas en la consola
Y eso por qué pasa?
Porque cada loop de forEach
es en realidad una función independiente, no forma parte de un loop
En otras palabras, el forEach lanza tantas funciones como elementos tenga en el loop, y estas se ejecutan en paralelo, no hay ninguna secuencia
Conclusión?
Si necesitas ejecutar el loop de forma secuencial, con forEach
no puedes
Tu elección es el loop for of
, y si no puedes con la programación imperativa, tu amigo es reduce()
Lista de correo: escribo algo de tanto en cuanto