Usando async / await con un bucle forEach

Jun 02 2016

¿Hay algún problema con el uso de async/ awaiten un forEachbucle? Estoy tratando de recorrer una matriz de archivos y awaitel 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 salir mal algo con esto? Alguien me dijo que se supone que no debes usar async/ awaiten una función de orden superior como esta, así que solo quería preguntar si había algún problema con esto.

Respuestas

2680 Bergi Jun 02 2016 at 02:02

Seguro que el código funciona, pero estoy bastante seguro de que no hace lo que esperas que haga. Simplemente dispara múltiples llamadas asincrónicas, pero la printFilesfunción regresa inmediatamente después de eso.

Leyendo en secuencia

Si desea leer los archivos en secuencia, no puede usarlosforEach . Simplemente use un for … ofbucle moderno en su lugar, en el que awaitfuncionará 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 asyncllamadas a la función de devolución de llamada devuelve una promesa, pero las está desechando en lugar de esperarlas. Solo use mapen su lugar, y puede 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)
  }));
}
274 FranciscoMateo Jun 15 2018 at 18:17

Con ES2018, puede simplificar enormemente todas las respuestas anteriores a:

async function printFiles () {
  const files = await getFilePaths()

  for await (const contents of fs.readFile(file, 'utf8')) {
    console.log(contents)
  }
}

Ver especificaciones: propuesta-iteración asíncrona


2018-09-10: 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: ES2018: iteración asincrónica

81 TimothyZorn Mar 27 2018 at 02:48

En lugar de Promise.allen conjunto con Array.prototype.map(que no garantiza el orden en el que Promisese resuelven los s), utilizo 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());
}
35 AntonioVal Jul 10 2017 at 15:15

El módulo p- iteration en npm implementa los métodos de iteración Array para que se puedan usar de una manera muy sencilla con async / await.

Un ejemplo con tu caso:

const { forEach } = require('p-iteration');
const fs = require('fs-promise');

(async function printFiles () {
  const files = await getFilePaths();

  await forEach(files, async (file) => {
    const contents = await fs.readFile(file, 'utf8');
    console.log(contents);
  });
})();
32 Matt Mar 22 2018 at 22:11

A continuación se muestran algunos forEachAsyncprototipos. Tenga en cuenta que los necesitará await:

Array.prototype.forEachAsync = async function (fn) {
    for (let t of this) { await fn(t) }
}

Array.prototype.forEachAsyncParallel = async function (fn) {
    await Promise.all(this.map(fn));
}

Tenga en cuenta que si bien puede incluir esto en su propio código, no debe incluirlo en las bibliotecas que distribuye a otros (para evitar contaminar sus globales).

9 chharvey Feb 23 2018 at 07:47

Además de la respuesta de @ Bergi , me gustaría ofrecer una tercera alternativa. Es muy similar al segundo ejemplo de @ Bergi, pero en lugar de esperar cada uno readFileindividualmente, creas una serie de promesas, cada una de las cuales esperas al final.

import fs from 'fs-promise';
async function printFiles () {
  const files = await getFilePaths();

  const promises = files.map((file) => fs.readFile(file, 'utf8'))

  const contents = await Promise.all(promises)

  contents.forEach(console.log);
}

Tenga en cuenta que la función pasada a .map()no tiene por qué ser así async, ya que fs.readFiledevuelve un objeto Promise de todos modos. Por promiseslo tanto, es una matriz de objetos Promise, a los que se puede enviar Promise.all().

En la respuesta de @ Bergi, la consola puede registrar el contenido del archivo en el orden en que se lee. Por ejemplo, si un archivo realmente pequeño termina de leerse antes que un archivo realmente grande, se registrará primero, incluso si el archivo pequeño viene después del archivo grande en la filesmatriz. Sin embargo, en mi método anterior, tiene la garantía de que la consola registrará los archivos en el mismo orden que la matriz proporcionada.

7 master_dodo May 27 2019 at 05:08

La solución de Bergi funciona bien cuando fsse basa en promesas. Puede usar bluebird, fs-extrao fs-promisepara esto.

Sin embargo, la solución para la fsbiblioteca nativa del nodo es la siguiente:

const result = await Promise.all(filePaths
    .map( async filePath => {
      const fileContents = await getAssetFromCache(filePath, async function() {

        // 1. Wrap with Promise    
        // 2. Return the result of the Promise
        return await new Promise((res, rej) => {
          fs.readFile(filePath, 'utf8', function(err, data) {
            if (data) {
              res(data);
            }
          });
        });
      });

      return fileContents;
    }));

Nota: require('fs') obligatoriamente toma la función como tercer argumento, de lo contrario arroja un error:

TypeError [ERR_INVALID_CALLBACK]: Callback must be a function
6 HoomanAskari Aug 26 2017 at 17:47

Ambas soluciones funcionan, sin embargo, Antonio hace el trabajo con menos código, así es como me ayudó a resolver los datos de mi base de datos, de varias referencias secundarias diferentes y luego empujarlas todas en una matriz y resolverlas en una promesa después de todo es hecho:

Promise.all(PacksList.map((pack)=>{
    return fireBaseRef.child(pack.folderPath).once('value',(snap)=>{
        snap.forEach( childSnap => {
            const file = childSnap.val()
            file.id = childSnap.key;
            allItems.push( file )
        })
    })
})).then(()=>store.dispatch( actions.allMockupItems(allItems)))
5 JayEdwards Sep 23 2017 at 06:03

Es bastante sencillo colocar un par de métodos en un archivo que manejará datos asincrónicos en un orden serializado y le dará un sabor más convencional a su código. Por ejemplo:

module.exports = function () {
  var self = this;

  this.each = async (items, fn) => {
    if (items && items.length) {
      await Promise.all(
        items.map(async (item) => {
          await fn(item);
        }));
    }
  };

  this.reduce = async (items, fn, initialValue) => {
    await self.each(
      items, async (item) => {
        initialValue = await fn(initialValue, item);
      });
    return initialValue;
  };
};

ahora, asumiendo que está guardado en './myAsync.js', puede hacer algo similar a lo siguiente en un archivo adyacente:

...
/* your server setup here */
...
var MyAsync = require('./myAsync');
var Cat = require('./models/Cat');
var Doje = require('./models/Doje');
var example = async () => {
  var myAsync = new MyAsync();
  var doje = await Doje.findOne({ name: 'Doje', noises: [] }).save();
  var cleanParams = [];

  // FOR EACH EXAMPLE
  await myAsync.each(['bork', 'concern', 'heck'], 
    async (elem) => {
      if (elem !== 'heck') {
        await doje.update({ $push: { 'noises': elem }});
      }
    });

  var cat = await Cat.findOne({ name: 'Nyan' });

  // REDUCE EXAMPLE
  var friendsOfNyanCat = await myAsync.reduce(cat.friends,
    async (catArray, friendId) => {
      var friend = await Friend.findById(friendId);
      if (friend.name !== 'Long cat') {
        catArray.push(friend.name);
      }
    }, []);
  // Assuming Long Cat was a friend of Nyan Cat...
  assert(friendsOfNyanCat.length === (cat.friends.length - 1));
}
5 OliverDixon Apr 17 2020 at 00:18

Esta solución también está optimizada para la memoria, por lo que puede ejecutarla en 10,000 elementos de datos y solicitudes. Algunas de las otras soluciones aquí bloquean el servidor en grandes conjuntos de datos.

En TypeScript:

export async function asyncForEach<T>(array: Array<T>, callback: (item: T, index: number) => void) {
        for (let index = 0; index < array.length; index++) {
            await callback(array[index], index);
        }
    }

¿Cómo utilizar?

await asyncForEach(receipts, async (eachItem) => {
    await ...
})
4 LeOn-HanLi Sep 25 2017 at 03:00

Una advertencia importante es: el await + for .. ofmétodo y la forEach + asyncforma en realidad tienen un efecto diferente.

Tener awaitdentro un forbucle real asegurará que todas las llamadas asíncronas se ejecuten una por una. Y el forEach + asynccamino disparará todas las promesas al mismo tiempo, lo que es más rápido pero a veces abrumador ( si realiza alguna consulta de base de datos o visita algunos servicios web con restricciones de volumen y no desea disparar 100.000 llamadas a la vez).

También puede usar reduce + promise(menos elegante) si no lo usa async/awaity desea asegurarse de que los archivos se lean uno tras otro .

files.reduce((lastPromise, file) => 
 lastPromise.then(() => 
   fs.readFile(file, 'utf8')
 ), Promise.resolve()
)

O puede crear un forEachAsync para ayudar, pero básicamente usar el mismo para el bucle subyacente.

Array.prototype.forEachAsync = async function(cb){
    for(let x of this){
        await cb(x);
    }
}
4 gsaandy Dec 01 2019 at 23:59

Solo agregando a la respuesta original

  • La sintaxis de lectura paralela en la respuesta original es a veces confusa y difícil de leer, tal vez podamos escribirla en un enfoque diferente
async function printFiles() {
  const files = await getFilePaths();
  const fileReadPromises = [];

  const readAndLogFile = async filePath => {
    const contents = await fs.readFile(file, "utf8");
    console.log(contents);
    return contents;
  };

  files.forEach(file => {
    fileReadPromises.push(readAndLogFile(file));
  });

  await Promise.all(fileReadPromises);
}

  • Para la operación secuencial, no solo para ... de , el bucle for normal también funcionará
async function printFiles() {
  const files = await getFilePaths();

  for (let i = 0; i < files.length; i++) {
    const file = files[i];
    const contents = await fs.readFile(file, "utf8");
    console.log(contents);
  }
}

4 lukaswilkeer Dec 21 2019 at 08:11

Como la respuesta de @ Bergi, pero con una diferencia.

Promise.all rechaza todas las promesas si una es rechazada.

Entonces, use una recursividad.

const readFilesQueue = async (files, index = 0) {
    const contents = await fs.readFile(files[index], 'utf8')
    console.log(contents)

    return files.length <= index
        ? readFilesQueue(files, ++index)
        : files

}

const printFiles async = () => {
    const files = await getFilePaths();
    const printContents = await readFilesQueue(files)

    return printContents
}

printFiles()

PD

readFilesQueueestá fuera de la printFilescausa del efecto secundario * introducido por console.log, es mejor burlarse, probar o espiar, por lo que no es bueno tener una función que devuelva el contenido (nota al margen).

Por lo tanto, el código puede diseñarse simplemente por eso: tres funciones separadas que son "puras" ** y no introducen efectos secundarios, procesan la lista completa y pueden modificarse fácilmente para manejar casos fallidos.

const files = await getFilesPath()

const printFile = async (file) => {
    const content = await fs.readFile(file, 'utf8')
    console.log(content)
}

const readFiles = async = (files, index = 0) => {
    await printFile(files[index])

    return files.lengh <= index
        ? readFiles(files, ++index)
        : files
}

readFiles(files)

Edición futura / estado actual

Node admite la espera de nivel superior (esto aún no tiene un complemento, no lo tendrá y se puede habilitar a través de indicadores de armonía), es genial pero no resuelve un problema (estratégicamente trabajo solo en versiones LTS). ¿Cómo conseguir los archivos?

Usando composición. Dado el código, me causa la sensación de que este está dentro de un módulo, por lo que debería tener una función para hacerlo. De lo contrario, debe usar un IIFE para envolver el código de función en una función asíncrona creando un módulo simple que lo haga todo por usted, o puede seguir la forma correcta, hay composición.

// more complex version with IIFE to a single module
(async (files) => readFiles(await files())(getFilesPath)

Tenga en cuenta que el nombre de la variable cambia debido a la semántica. Pasas un functor (una función que puede ser invocada por otra función) y recibes un puntero en la memoria que contiene el bloque inicial de lógica de la aplicación.

Pero, ¿no es un módulo y necesitas exportar la lógica?

Envuelva las funciones en una función asincrónica.

export const readFilesQueue = async () => {
    // ... to code goes here
}

O cambiar los nombres de las variables, lo que sea ...


* por efecto secundario menans cualquier efecto colacteral de la aplicación que pueda cambiar el estado / comportamiento o introducir errores en la aplicación, como IO.

** por "puro", está en apóstrofo ya que las funciones no son puras y el código puede converger a una versión pura, cuando no hay salida de consola, solo manipulaciones de datos.

Aparte de esto, para ser puro, necesitará trabajar con mónadas que manejen el efecto secundario, que sean propensas a errores, y traten ese error por separado de la aplicación.

3 Babakness Feb 28 2018 at 11:41

Usando Task, futurize y una lista transitable, simplemente puede hacer

async function printFiles() {
  const files = await getFiles();

  List(files).traverse( Task.of, f => readFile( f, 'utf-8'))
    .fork( console.error, console.log)
}

Así es como lo configuraría

import fs from 'fs';
import { futurize } from 'futurize';
import Task from 'data.task';
import { List } from 'immutable-ext';

const future = futurizeP(Task)
const readFile = future(fs.readFile)

Otra forma de haber estructurado el código deseado sería

const printFiles = files => 
  List(files).traverse( Task.of, fn => readFile( fn, 'utf-8'))
    .fork( console.error, console.log)

O quizás incluso más funcionalmente orientado

// 90% of encodings are utf-8, making that use case super easy is prudent

// handy-library.js
export const readFile = f =>
  future(fs.readFile)( f, 'utf-8' )

export const arrayToTaskList = list => taskFn => 
  List(files).traverse( Task.of, taskFn ) 

export const readFiles = files =>
  arrayToTaskList( files, readFile )

export const printFiles = files => 
  readFiles(files).fork( console.error, console.log)

Luego de la función padre

async function main() {
  /* awesome code with side-effects before */
  printFiles( await getFiles() );
  /* awesome code with side-effects after */
}

Si realmente desea más flexibilidad en la codificación, puede hacer esto (por diversión, estoy usando el operador Pipe Forward propuesto )

import { curry, flip } from 'ramda'

export const readFile = fs.readFile 
  |> future,
  |> curry,
  |> flip

export const readFileUtf8 = readFile('utf-8')

PD: no probé este código en la consola, podría tener algunos errores tipográficos ... "¡estilo libre directo, desde la parte superior del domo!" como dirían los niños de los 90. :-pag

3 Beau Mar 13 2019 at 06:31

Actualmente, la propiedad del prototipo Array.forEach no admite operaciones asincrónicas, pero podemos crear nuestro propio poly-fill para satisfacer nuestras necesidades.

// Example of asyncForEach Array poly-fill for NodeJs
// file: asyncForEach.js
// Define asynForEach function 
async function asyncForEach(iteratorFunction){
  let indexer = 0
  for(let data of this){
    await iteratorFunction(data, indexer)
    indexer++
  }
}
// Append it as an Array prototype property
Array.prototype.asyncForEach = asyncForEach
module.exports = {Array}

¡Y eso es! Ahora tiene un método async forEach disponible en cualquier matriz que se defina después de estas para operaciones.

Vamos a probarlo ...

// Nodejs style
// file: someOtherFile.js

const readline = require('readline')
Array = require('./asyncForEach').Array
const log = console.log

// Create a stream interface
function createReader(options={prompt: '>'}){
  return readline.createInterface({
    input: process.stdin
    ,output: process.stdout
    ,prompt: options.prompt !== undefined ? options.prompt : '>'
  })
}
// Create a cli stream reader
async function getUserIn(question, options={prompt:'>'}){
  log(question)
  let reader = createReader(options)
  return new Promise((res)=>{
    reader.on('line', (answer)=>{
      process.stdout.cursorTo(0, 0)
      process.stdout.clearScreenDown()
      reader.close()
      res(answer)
    })
  })
}

let questions = [
  `What's your name`
  ,`What's your favorite programming language`
  ,`What's your favorite async function`
]
let responses = {}

async function getResponses(){
// Notice we have to prepend await before calling the async Array function
// in order for it to function as expected
  await questions.asyncForEach(async function(question, index){
    let answer = await getUserIn(question)
    responses[question] = answer
  })
}

async function main(){
  await getResponses()
  log(responses)
}
main()
// Should prompt user for an answer to each question and then 
// log each question and answer as an object to the terminal

Podríamos hacer lo mismo para algunas de las otras funciones de matriz como map ...

async function asyncMap(iteratorFunction){
  let newMap = []
  let indexer = 0
  for(let data of this){
    newMap[indexer] = await iteratorFunction(data, indexer, this)
    indexer++
  }
  return newMap
}

Array.prototype.asyncMap = asyncMap

... y así :)

Algunas cosas a tener en cuenta:

  • Su iteratorFunction debe ser una función o promesa asincrónica
  • Cualquier arreglo creado antes Array.prototype.<yourAsyncFunc> = <yourAsyncFunc>no tendrá esta función disponible
3 PranavKAndro Nov 25 2019 at 03:31

Hoy encontré múltiples soluciones para esto. Ejecutando las funciones asíncronas en espera en el bucle forEach. Construyendo el envoltorio podemos hacer que esto suceda.

En el enlace aquí se proporciona una explicación más detallada sobre cómo funciona internamente, para el forEach nativo y por qué no puede realizar una llamada de función asíncrona y otros detalles sobre los diversos métodos.

Las múltiples formas a través de las cuales se puede hacer y son las siguientes,

Método 1: usando la envoltura.

await (()=>{
     return new Promise((resolve,reject)=>{
       items.forEach(async (item,index)=>{
           try{
               await someAPICall();
           } catch(e) {
              console.log(e)
           }
           count++;
           if(index === items.length-1){
             resolve('Done')
           }
         });
     });
    })();

Método 2: usar lo mismo que una función genérica de Array.prototype

Array.prototype.forEachAsync.js

if(!Array.prototype.forEachAsync) {
    Array.prototype.forEachAsync = function (fn){
      return new Promise((resolve,reject)=>{
        this.forEach(async(item,index,array)=>{
            await fn(item,index,array);
            if(index === array.length-1){
                resolve('done');
            }
        })
      });
    };
  }

Uso:

require('./Array.prototype.forEachAsync');

let count = 0;

let hello = async (items) => {

// Method 1 - Using the Array.prototype.forEach 

    await items.forEachAsync(async () => {
         try{
               await someAPICall();
           } catch(e) {
              console.log(e)
           }
        count++;
    });

    console.log("count = " + count);
}

someAPICall = () => {
    return new Promise((resolve, reject) => {
        setTimeout(() => {
            resolve("done") // or reject('error')
        }, 100);
    })
}

hello(['', '', '', '']); // hello([]) empty array is also be handled by default

Método 3:

Usando Promise.all

  await Promise.all(items.map(async (item) => {
        await someAPICall();
        count++;
    }));

    console.log("count = " + count);

Método 4: bucle for tradicional o bucle for moderno

// Method 4 - using for loop directly

// 1. Using the modern for(.. in..) loop
   for(item in items){

        await someAPICall();
        count++;
    }

//2. Using the traditional for loop 

    for(let i=0;i<items.length;i++){

        await someAPICall();
        count++;
    }


    console.log("count = " + count);
3 richytong May 21 2020 at 03:57

Puede usar Array.prototype.forEach, pero async / await no es tan compatible. Esto se debe a que la promesa devuelta de una devolución de llamada asincrónica espera resolverse, pero Array.prototype.forEachno resuelve ninguna promesa de la ejecución de su devolución de llamada. Entonces, puede usar forEach, pero tendrá que manejar la resolución de la promesa usted mismo.

Aquí hay una forma de leer e imprimir cada archivo en serie usando Array.prototype.forEach

async function printFilesInSeries () {
  const files = await getFilePaths()

  let promiseChain = Promise.resolve()
  files.forEach((file) => {
    promiseChain = promiseChain.then(() => {
      fs.readFile(file, 'utf8').then((contents) => {
        console.log(contents)
      })
    })
  })
  await promiseChain
}

Aquí hay una forma (que todavía se usa Array.prototype.forEach) de imprimir el contenido de los archivos en paralelo

async function printFilesInParallel () {
  const files = await getFilePaths()

  const promises = []
  files.forEach((file) => {
    promises.push(
      fs.readFile(file, 'utf8').then((contents) => {
        console.log(contents)
      })
    )
  })
  await Promise.all(promises)
}
2 jgmjgm Oct 15 2019 at 01:35

Para ver cómo puede salir mal, imprima console.log al final del método.

Cosas que pueden salir mal en general:

  • Orden arbitrario.
  • printFiles puede terminar de ejecutarse antes de imprimir archivos.
  • Bajo rendimiento.

Estos no siempre son incorrectos, pero con frecuencia se encuentran en casos de uso estándar.

Generalmente, el uso de forEach dará como resultado todos menos el último. Llamará a cada función sin esperar la función, lo que significa que le dice a todas las funciones que comiencen y luego finalice sin esperar a que terminen las funciones.

import fs from 'fs-promise'

async function printFiles () {
  const files = (await getFilePaths()).map(file => fs.readFile(file, 'utf8'))

  for(const file of files)
    console.log(await file)
}

printFiles()

Este es un ejemplo en JS nativo que preservará el orden, evitará que la función regrese prematuramente y, en teoría, conservará un rendimiento óptimo.

Esta voluntad:

  • Inicie todas las lecturas de archivos para que sucedan en paralelo.
  • Conserve el orden mediante el uso del mapa para asignar nombres de archivos a las promesas de espera.
  • Espere cada promesa en el orden definido por la matriz.

Con esta solución, el primer archivo se mostrará tan pronto como esté disponible sin tener que esperar a que los demás estén disponibles primero.

También cargará todos los archivos al mismo tiempo en lugar de tener que esperar a que termine el primero antes de que se pueda iniciar la lectura del segundo archivo.

El único inconveniente de esto y la versión original es que si se inician varias lecturas a la vez, es más difícil manejar los errores debido a que hay más errores que pueden ocurrir a la vez.

Con las versiones que leen un archivo a la vez, luego se detendrá en caso de falla sin perder tiempo tratando de leer más archivos. Incluso con un sistema de cancelación elaborado, puede ser difícil evitar que falle en el primer archivo, pero también leer la mayoría de los otros archivos.

El rendimiento no siempre es predecible. Si bien muchos sistemas serán más rápidos con lecturas de archivos paralelas, algunos preferirán la secuencial. Algunos son dinámicos y pueden cambiar bajo carga, las optimizaciones que ofrecen latencia no siempre producen un buen rendimiento bajo una fuerte contención.

Tampoco hay manejo de errores en ese ejemplo. Si algo requiere que se muestren correctamente o no se muestren, no lo hará.

Se recomienda experimentar en profundidad con console.log en cada etapa y soluciones de lectura de archivos falsos (en su lugar, retraso aleatorio). Aunque muchas soluciones parecen hacer lo mismo en casos simples, todas tienen diferencias sutiles que requieren un escrutinio adicional para exprimirlas.

Utilice este simulacro para ayudar a diferenciar las soluciones:

(async () => {
  const start = +new Date();
  const mock = () => {
    return {
      fs: {readFile: file => new Promise((resolve, reject) => {
        // Instead of this just make three files and try each timing arrangement.
        // IE, all same, [100, 200, 300], [300, 200, 100], [100, 300, 200], etc.
        const time = Math.round(100 + Math.random() * 4900);
        console.log(`Read of ${file} started at ${new Date() - start} and will take ${time}ms.`)
        setTimeout(() => {
          // Bonus material here if random reject instead.
          console.log(`Read of ${file} finished, resolving promise at ${new Date() - start}.`);
          resolve(file);
        }, time);
      })},
      console: {log: file => console.log(`Console Log of ${file} finished at ${new Date() - start}.`)},
      getFilePaths: () => ['A', 'B', 'C', 'D', 'E']
    };
  };

  const printFiles = (({fs, console, getFilePaths}) => {
    return async function() {
      const files = (await getFilePaths()).map(file => fs.readFile(file, 'utf8'));

      for(const file of files)
        console.log(await file);
    };
  })(mock());

  console.log(`Running at ${new Date() - start}`);
  await printFiles();
  console.log(`Finished running at ${new Date() - start}`);
})();

ScottRudiger Jun 21 2018 at 23:55

Similar al de Antonio Val p-iteration, un módulo npm alternativo es async-af:

const AsyncAF = require('async-af');
const fs = require('fs-promise');

function printFiles() {
  // since AsyncAF accepts promises or non-promises, there's no need to await here
  const files = getFilePaths();

  AsyncAF(files).forEach(async file => {
    const contents = await fs.readFile(file, 'utf8');
    console.log(contents);
  });
}

printFiles();

Alternativamente, async-aftiene un método estático (log / logAF) que registra los resultados de las promesas:

const AsyncAF = require('async-af');
const fs = require('fs-promise');

function printFiles() {
  const files = getFilePaths();

  AsyncAF(files).forEach(file => {
    AsyncAF.log(fs.readFile(file, 'utf8'));
  });
}

printFiles();

Sin embargo, la principal ventaja de la biblioteca es que puede encadenar métodos asincrónicos para hacer algo como:

const aaf = require('async-af');
const fs = require('fs-promise');

const printFiles = () => aaf(getFilePaths())
  .map(file => fs.readFile(file, 'utf8'))
  .forEach(file => aaf.log(file));

printFiles();

async-af