Usando async / await com um loop forEach

Jun 02 2016

Há algum problema com o uso de async/ awaitem um forEachloop? Estou tentando percorrer uma série de arquivos e awaito conteúdo de cada arquivo.

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, mas pode haver algo errado com isso? Alguém me disse que você não deveria usar async/ awaitem uma função de ordem superior como esta, então eu só queria perguntar se havia algum problema com isso.

Respostas

2680 Bergi Jun 02 2016 at 02:02

Claro que o código funciona, mas tenho quase certeza de que não faz o que você espera. Ele apenas dispara várias chamadas assíncronas, mas a printFilesfunção retorna imediatamente depois disso.

Leitura em sequência

Se você quiser ler os arquivos em sequência, não poderá usarforEach . Basta usar um for … ofloop moderno , no qual awaitfuncionará conforme o esperado:

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

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

Lendo em paralelo

Se você quiser ler os arquivos em paralelo, não poderá usá-los deforEach fato. Cada uma das asyncchamadas de função de retorno de chamada retorna uma promessa, mas você as está descartando em vez de aguardá-las. Em mapvez disso, use e você poderá aguardar a série de promessas que receberá com 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

Com ES2018, você é capaz de simplificar muito todas as respostas acima para:

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

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

Veja as especificações: proposta-async-iteration


10/09/2018: Esta resposta tem recebido muita atenção recentemente, consulte a postagem do blog de Axel Rauschmayer para obter mais informações sobre iteração assíncrona: ES2018: iteração assíncrona

81 TimothyZorn Mar 27 2018 at 02:48

Em vez de Promise.allem conjunto com Array.prototype.map(o que não garante a ordem em que os Promises são resolvidos), uso Array.prototype.reduce, começando com um resolvido 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

O módulo p-iteration no npm implementa os métodos de iteração Array para que eles possam ser usados ​​de uma maneira muito direta com async / await.

Um exemplo com o seu 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

Aqui estão alguns forEachAsyncprotótipos. Observe que você precisará awaitdeles:

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));
}

Observe que, embora você possa incluir isso em seu próprio código, você não deve incluí-lo em bibliotecas distribuídas para outras pessoas (para evitar poluir seus globais).

9 chharvey Feb 23 2018 at 07:47

Além da resposta de @ Bergi , gostaria de oferecer uma terceira alternativa. É muito semelhante ao segundo exemplo de @ Bergi, mas em vez de esperar cada uma readFileindividualmente, você cria uma série de promessas, cada uma das quais espera no 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);
}

Observe que a função passada para .map()não precisa ser async, pois fs.readFileretorna um objeto Promise de qualquer maneira. Portanto, promisesé uma matriz de objetos Promise, que podem ser enviados para Promise.all().

Na resposta de @ Bergi, o console pode registrar o conteúdo do arquivo na ordem em que são lidos. Por exemplo, se um arquivo muito pequeno terminar a leitura antes de um arquivo muito grande, ele será registrado primeiro, mesmo se o arquivo pequeno vier depois do arquivo grande no filesarray. No entanto, no meu método acima, você tem a garantia de que o console registrará os arquivos na mesma ordem do array fornecido.

7 master_dodo May 27 2019 at 05:08

A solução de Bergi funciona bem quando fsé baseada em promessas. Você pode usar bluebird, fs-extraou fs-promisepara isso.

No entanto, a solução para a fsbiblioteca nativa do nó é a seguinte:

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') obrigatoriamente assume a função como terceiro argumento, caso contrário, gera erro:

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

Ambas as soluções acima funcionam, no entanto, Antonio faz o trabalho com menos código, aqui está como ele me ajudou a resolver os dados do meu banco de dados, de vários refs filhos diferentes e, em seguida, empurrando todos em um array e resolvendo em uma promessa, feito:

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

é muito fácil inserir alguns métodos em um arquivo que manipulará dados assíncronos em uma ordem serializada e fornecerá um sabor mais convencional ao seu código. Por exemplo:

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;
  };
};

agora, supondo que esteja salvo em './myAsync.js', você pode fazer algo semelhante ao abaixo em um arquivo adjacente:

...
/* 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 solução também é otimizada para memória para que você possa executá-la em 10.000 itens de dados e solicitações. Algumas das outras soluções aqui irão travar o servidor em grandes conjuntos de dados.

Em 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);
        }
    }

Como usar?

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

Uma advertência importante é: o await + for .. ofmétodo e a forEach + asyncmaneira na verdade têm efeitos diferentes.

Ter awaitdentro de um forloop real garantirá que todas as chamadas assíncronas sejam executadas uma por uma. E o forEach + asynccaminho vai disparar todas as promessas ao mesmo tempo, o que é mais rápido, mas às vezes sobrecarregado ( se você fizer alguma consulta no banco de dados ou visitar alguns serviços da web com restrições de volume e não quiser disparar 100.000 chamadas de uma vez).

Você também pode usar reduce + promise(menos elegante) se não usar async/awaite quiser garantir que os arquivos sejam lidos um após o outro .

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

Ou você pode criar um forEachAsync para ajudar, mas basicamente usar o mesmo for loop subjacente.

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

Apenas adicionando à resposta original

  • A sintaxe de leitura paralela na resposta original às vezes é confusa e difícil de ler, talvez possamos escrevê-la em uma abordagem 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 operação sequencial, não apenas para ... de , o loop normal para também 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 a resposta de @ Bergi, mas com uma diferença.

Promise.all rejeita todas as promessas se uma for rejeitada.

Portanto, use uma recursão.

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()

PS

readFilesQueueestá fora da printFilescausa o efeito colateral * introduzido por console.log, é melhor simular, testar e / ou espiar, portanto, não é legal ter uma função que retorna o conteúdo (nota lateral).

Portanto, o código pode ser simplesmente projetado por isso: três funções separadas que são "puras" ** e não apresentam efeitos colaterais, processam a lista inteira e podem ser facilmente modificadas para lidar com casos de falha.

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)

Edição futura / estado atual

O Node suporta await de nível superior (isso ainda não tem um plugin, não terá e pode ser habilitado por meio de flags de harmonia), é legal, mas não resolve um problema (estrategicamente, trabalho apenas em versões LTS). Como obter os arquivos?

Usando composição. Dado o código, me dá a sensação de que ele está dentro de um módulo, portanto, deveria ter uma função para fazê-lo. Caso contrário, você deve usar um IIFE para envolver o código de função em uma função assíncrona, criando um módulo simples que faz tudo para você, ou você pode seguir o caminho certo, há, composição.

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

Observe que o nome da variável muda devido à semântica. Você passa um functor (uma função que pode ser invocada por outra função) e recebe um ponteiro na memória que contém o bloco inicial de lógica da aplicação.

Mas, se não for um módulo e você precisa exportar a lógica?

Envolva as funções em uma função assíncrona.

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

Ou mude os nomes das variáveis, o que for ...


* por efeito colateral significa qualquer efeito colacteral do aplicativo que pode alterar o estado / comportamento ou introduzir bugs no aplicativo, como IO.

** por "puro", está em apóstrofo, pois as funções não são puras e o código pode ser convergido para uma versão pura, quando não há saída do console, apenas manipulação de dados.

Além disso, para ser puro, você precisará trabalhar com mônadas que tratam do efeito colateral, que são propensas a erros, e tratam esse erro separadamente do aplicativo.

3 Babakness Feb 28 2018 at 11:41

Usando Task, futurize e uma lista percorrível, você pode simplesmente fazer

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

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

Aqui está como você configuraria isso

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)

Outra forma de estruturar o código desejado seria

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

Ou talvez ainda mais 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)

Então, da função pai

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

Se você realmente quiser mais flexibilidade na codificação, pode simplesmente fazer isso (para se divertir, estou usando o operador Pipe Forward proposto )

import { curry, flip } from 'ramda'

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

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

PS - Eu não tentei este código no console, pode haver alguns erros de digitação ... "estilo livre direto, fora do topo da cúpula!" como diriam as crianças dos anos 90. :-p

3 Beau Mar 13 2019 at 06:31

Atualmente, a propriedade de protótipo Array.forEach não oferece suporte a operações assíncronas, mas podemos criar nosso próprio poli-preenchimento para atender às nossas necessidades.

// 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}

E é isso! Agora você tem um método async forEach disponível em quaisquer matrizes definidas após essas operações.

Vamos testar ...

// 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

Poderíamos fazer o mesmo para algumas das outras funções de array, 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

... e assim por diante :)

Algumas coisas a serem observadas:

  • Seu iteratorFunction deve ser uma função assíncrona ou promessa
  • Todas as matrizes criadas antes Array.prototype.<yourAsyncFunc> = <yourAsyncFunc>não terão esse recurso disponível
3 PranavKAndro Nov 25 2019 at 03:31

Hoje me deparei com várias soluções para isso. Executando as funções async await no forEach Loop. Construindo o invólucro, podemos fazer isso acontecer.

Uma explicação mais detalhada sobre como funciona internamente, para o forEach nativo e porque não é capaz de fazer uma chamada de função assíncrona e outros detalhes sobre os vários métodos são fornecidos no link aqui

As várias maneiras pelas quais isso pode ser feito e são as seguintes,

Método 1: usando o invólucro.

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: usando o mesmo que uma função 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: loop for tradicional ou loop 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

Você pode usar Array.prototype.forEach, mas async / await não é tão compatível. Isso ocorre porque a promessa retornada de um retorno de chamada assíncrono espera ser resolvida, mas Array.prototype.forEachnão resolve nenhuma promessa da execução de seu retorno de chamada. Então, você pode usar forEach, mas terá que lidar com a resolução da promessa sozinho.

Aqui está uma maneira de ler e imprimir cada arquivo em série 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
}

Aqui está uma maneira (ainda usando Array.prototype.forEach) para imprimir o conteúdo dos arquivos em 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 como isso pode dar errado, imprima console.log no final do método.

Coisas que podem dar errado em geral:

  • Ordem arbitrária.
  • printFiles pode terminar a execução antes de imprimir os arquivos.
  • Desempenho ruim.

Nem sempre estão errados, mas frequentemente estão em casos de uso padrão.

Geralmente, o uso de forEach resultará em todos, exceto no último. Ele chamará cada função sem esperar pela função, o que significa que diz a todas as funções para iniciar e termina sem esperar que as funções terminem.

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 é um exemplo em JS nativo que preservará a ordem, evitará que a função retorne prematuramente e, em teoria, manterá o desempenho ideal.

Isso vai:

  • Inicie todas as leituras de arquivo para acontecer em paralelo.
  • Preserve a ordem por meio do uso de mapa para mapear nomes de arquivo para promessas a aguardar.
  • Aguarde cada promessa na ordem definida pela matriz.

Com esta solução, o primeiro arquivo será mostrado assim que estiver disponível, sem ter que esperar que os outros estejam disponíveis primeiro.

Ele também carregará todos os arquivos ao mesmo tempo, em vez de ter que esperar o primeiro terminar antes que a segunda leitura de arquivo possa ser iniciada.

A única desvantagem disso e da versão original é que, se várias leituras forem iniciadas ao mesmo tempo, será mais difícil lidar com os erros, pois há mais erros que podem ocorrer de uma vez.

Com versões que leem um arquivo por vez, então irão parar em caso de falha, sem perder tempo tentando ler mais nenhum arquivo. Mesmo com um sistema de cancelamento elaborado, pode ser difícil evitar que ele falhe no primeiro arquivo, mas também já lê a maioria dos outros arquivos.

O desempenho nem sempre é previsível. Embora muitos sistemas sejam mais rápidos com leituras de arquivos paralelas, alguns preferem sequencial. Alguns são dinâmicos e podem mudar sob carga, otimizações que oferecem latência nem sempre rendem um bom rendimento sob forte contenção.

Também não há tratamento de erros nesse exemplo. Se algo exige que todos eles sejam mostrados com sucesso ou não, isso não acontecerá.

Recomenda-se uma experimentação profunda com console.log em cada estágio e soluções de leitura de arquivos falsos (em vez disso, atraso aleatório). Embora muitas soluções pareçam fazer o mesmo em casos simples, todas têm diferenças sutis que exigem um exame mais minucioso para serem eliminadas.

Use esta simulação para ajudar a diferenciar as soluções:

(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

Semelhante ao de Antonio Val p-iteration, um módulo alternativo de npm é 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();

Como alternativa, async-aftem um método estático (log / logAF) que registra os resultados das promessas:

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();

No entanto, a principal vantagem da biblioteca é que você pode encadear métodos assíncronos para fazer 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