Usando async/await con un bucle forEach
¿Hay algún problema con el uso async
de / await
en un forEach
bucle? Estoy intentando recorrer una serie de archivos y await
el contenido de cada archivo.
import fs from 'fs-promise'
async function printFiles () {
const files = await getFilePaths() // Assume this works fine
files.forEach(async (file) => {
const contents = await fs.readFile(file, 'utf8')
console.log(contents)
})
}
printFiles()
Este código funciona, pero ¿podría haber algún problema con esto? Alguien me dijo que se supone que no debes usar async
/ await
en una función de orden superior como esta, así que solo quería preguntar si había algún problema con esto.
Seguro que el código funciona, pero estoy bastante seguro de que no hace lo que esperas. Simplemente activa múltiples llamadas asincrónicas, pero la printFiles
función regresa inmediatamente después de eso.
Leyendo en secuencia
Si desea leer los archivos en secuencia, no puede usarlosforEach
. Simplemente use un for … of
bucle moderno en su lugar, en el que await
funcionará como se esperaba:
async function printFiles () {
const files = await getFilePaths();
for (const file of files) {
const contents = await fs.readFile(file, 'utf8');
console.log(contents);
}
}
Leyendo en paralelo
Si desea leer los archivos en paralelo, no puede usarlosforEach
. Cada una de las async
llamadas a la función de devolución de llamada devuelve una promesa, pero las estás desechando en lugar de esperarlas. Simplemente utilícelo map
en su lugar y podrá esperar la variedad de promesas que obtendrá con Promise.all
:
async function printFiles () {
const files = await getFilePaths();
await Promise.all(files.map(async (file) => {
const contents = await fs.readFile(file, 'utf8')
console.log(contents)
}));
}
Con ES2018, podrá simplificar enormemente todas las respuestas anteriores para:
async function printFiles () {
const files = await getFilePaths()
for await (const contents of files.map(file => fs.readFile(file, 'utf8'))) {
console.log(contents)
}
}
Ver especificación: propuesta-async-iteración
Simplificado:
for await (const results of array) {
await longRunningTask()
}
console.log('I will wait')
10/09/2018: Esta respuesta ha recibido mucha atención recientemente; consulte la publicación del blog de Axel Rauschmayer para obtener más información sobre la iteración asincrónica.
En lugar de Promise.all
en conjunto con Array.prototype.map
(lo que no garantiza el orden en el que Promise
se resuelven los s), uso Array.prototype.reduce
, comenzando con un resuelto Promise
:
async function printFiles () {
const files = await getFilePaths();
await files.reduce(async (promise, file) => {
// This line will wait for the last async function to finish.
// The first iteration uses an already resolved Promise
// so, it will immediately continue.
await promise;
const contents = await fs.readFile(file, 'utf8');
console.log(contents);
}, Promise.resolve());
}