forEach 루프와 함께 async / await 사용

Jun 02 2016

루프 에서 async/ 사용에 문제가 있습니까? 파일 배열과 각 파일의 내용 을 반복하려고 합니다.awaitforEachawait

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

이 코드는 작동하지만 문제가 발생할 수 있습니까? 누군가 이런 고차 기능에서 async/ 를 사용해서는 안된다고 말해 주었기 await때문에 이것에 문제가 있는지 물어보고 싶었습니다.

답변

2680 Bergi Jun 02 2016 at 02:02

물론 코드는 작동하지만 예상 한대로 작동하지 않는다고 확신합니다. 여러 비동기 호출을 시작하지만 그 printFiles후에 함수가 즉시 반환됩니다.

순서대로 읽기

순서대로 파일을 읽으려면 실제로 사용할 수 없습니다forEach . for … of대신 await예상대로 작동 하는 최신 루프를 사용하십시오 .

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

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

병렬로 읽기

파일을 병렬로 읽으려면 실제로 사용할 수 없습니다forEach . 각 async콜백 함수 호출은 프라 미스를 반환하지만이를 기다리는 대신 버립니다. map대신 사용 하면 다음과 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

ES2018을 사용하면 위의 모든 답변을 크게 단순화 할 수 있습니다.

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

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

사양 참조 : 제안 비동기 반복


2018-09-10 :이 답변은 최근 많은 관심을 받고 있습니다 . 비동기 반복 에 대한 자세한 내용은 Axel Rauschmayer의 블로그 게시물을 참조하세요. ES2018 : 비동기 반복

81 TimothyZorn Mar 27 2018 at 02:48

Promise.all와 함께 Array.prototype.map( Promises가 해결 되는 순서를 보장하지 않음) 대신 resolved로 Array.prototype.reduce시작하여를 사용합니다 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

npm 의 p-iteration 모듈은 async / await와 함께 매우 간단한 방식으로 사용할 수 있도록 Array 반복 메서드를 구현합니다.

케이스의 예 :

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

다음은 몇 가지 forEachAsync프로토 타입입니다. 다음이 필요 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));
}

참고 자신의 코드에서이 문제를 포함 할 수 있지만, 당신이 당신이 (자신의 전역을 오염을 방지하기 위해) 다른 사람에게 배포 라이브러리에 포함하지 않아야합니다.

9 chharvey Feb 23 2018 at 07:47

@Bergi의 답변 외에도 세 번째 대안을 제공하고 싶습니다. @Bergi의 두 번째 예제와 매우 비슷하지만 각각을 readFile개별적 으로 기다리는 대신 끝에 각각 기다리는 약속 배열을 만듭니다.

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

어쨌든 Promise 객체를 반환 하므로에 전달 된 함수는 일 .map()필요가 없습니다 . 따라서 으로 보낼 수있는 Promise 객체의 배열입니다 .asyncfs.readFilepromisesPromise.all()

@Bergi의 대답에서 콘솔은 읽은 순서대로 파일 내용을 기록 할 수 있습니다. 예를 들어, 정말 작은 파일이 정말 큰 파일보다 먼저 읽기를 마치면 작은 파일이 배열 의 큰 파일 뒤에 오는 경우에도 먼저 기록 files됩니다. 그러나 위의 방법에서 콘솔은 제공된 배열과 동일한 순서로 파일을 기록합니다.

7 master_dodo May 27 2019 at 05:08

Bergi의 솔루션fs 은 약속 기반 일 때 잘 작동합니다 . bluebird, fs-extra또는이 fs-promise를 사용할 수 있습니다 .

그러나 노드의 기본 fs라이브러리에 대한 솔루션 은 다음과 같습니다.

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

참고 : require('fs') 강제로 세 번째 인수로 함수를 사용하고 그렇지 않으면 오류가 발생합니다.

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

위의 두 솔루션 모두 작동하지만 Antonio 's는 더 적은 코드로 작업을 수행합니다. 여기에 내 데이터베이스의 데이터, 여러 하위 참조의 데이터를 확인한 다음 모두를 배열로 푸시하고 결국 약속에서 해결하는 데 도움이 된 방법이 있습니다. 끝난:

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

직렬화 된 순서로 비동기 데이터를 처리하고 코드에보다 일반적인 풍미를 제공하는 파일에 몇 가지 메서드를 팝하는 것은 매우 어렵지 않습니다. 예를 들면 :

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

이제 './myAsync.js'에 저장되었다고 가정하면 인접한 파일에서 아래와 유사한 작업을 수행 할 수 있습니다.

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

이 솔루션은 또한 메모리에 최적화되어 있으므로 10,000 개의 데이터 항목 및 요청에서 실행할 수 있습니다. 여기에있는 다른 솔루션 중 일부는 대용량 데이터 세트에서 서버를 중단시킵니다.

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

사용하는 방법?

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

한 가지 중요한 주의 사항await + for .. of방법과 forEach + async방식이 실제로 다른 효과를 갖는다는 것입니다.

await실제 for루프 안에 있으면 모든 비동기 호출이 하나씩 실행됩니다. 그리고이 forEach + async방법은 모든 약속을 동시에 실행합니다. 이는 빠르지 만 때로는 압도되는 경우도 있습니다 ( DB 쿼리를 수행하거나 볼륨 제한이있는 일부 웹 서비스를 방문 하고 한 번에 100,000 개의 호출을 실행하고 싶지 않은 경우).

당신은 또한 사용할 수 있습니다 reduce + promise(덜 우아) 사용하지 않는 경우 async/await있는지 파일을 읽어 만들고 싶어 연이어 .

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

또는 forEachAsync를 만들어 도움을 주지만 기본적으로 동일한 for 루프를 기본으로 사용할 수 있습니다.

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

원래 답변에 추가

  • 원래 답변의 병렬 읽기 구문은 때때로 혼란스럽고 읽기 어렵습니다. 다른 접근 방식으로 작성할 수 있습니다.
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);
}

  • for ... of 뿐만 아니라 순차적 작업 경우 일반 for 루프도 작동합니다.
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

@Bergi의 응답과 비슷하지만 한 가지 차이점이 있습니다.

Promise.all 하나가 거부되면 모든 약속을 거부합니다.

따라서 재귀를 사용하십시오.

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

추신

readFilesQueueprintFiles의해 도입 된 부작용 * 이 원인을 벗어 console.log났으므로 조롱, 테스트 또는 스파이하는 것이 더 낫습니다. 콘텐츠 (사이드 노트)를 반환하는 함수를 갖는 것은 멋지지 않습니다.

따라서 코드는 "순수"**하고 부작용이없고 전체 목록을 처리하며 실패한 경우를 처리하도록 쉽게 수정할 수있는 세 개의 분리 된 함수로 간단히 설계 할 수 있습니다.

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)

향후 편집 / 현재 상태

노드는 최상위 수준의 대기를 지원합니다 (아직 플러그인이없고 조화 플래그를 통해 활성화 할 수 있음). 멋지지만 한 가지 문제를 해결하지 못합니다 (전략적으로 LTS 버전에서만 작동합니다). 파일을 얻는 방법?

구성 사용. 코드가 주어지면 이것이 모듈 내부에 있다는 느낌을 주므로이를 수행하는 기능이 있어야합니다. 그렇지 않은 경우 IIFE를 사용하여 역할 코드를 비동기 함수로 래핑하여 모든 작업을 수행하는 간단한 모듈을 만들거나 올바른 방법으로 구성 할 수 있습니다.

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

의미론으로 인해 변수 이름이 변경됩니다. functor (다른 함수에 의해 호출 될 수있는 함수)를 전달하고 애플리케이션의 초기 논리 블록을 포함하는 메모리에 대한 포인터를 수신합니다.

하지만 모듈이 아니고 로직을 내 보내야하나요?

함수를 비동기 함수로 래핑합니다.

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

또는 변수 이름을 변경하십시오.


* 부작용에 의해 IO와 같은 응용 프로그램의 상태 / 행동 또는 인트로 스 버그를 변경할 수있는 응용 프로그램의 모든 공동 효과.

** "순수한"함수는 아포스트로피 안에 있습니다. 그 이유는 함수가 순수하지 않고 코드가 콘솔 출력이없고 데이터 조작 만있을 때 순수 버전으로 수렴 될 수 있기 때문입니다.

이 외에도 순수하기 위해서는 오류가 발생하기 쉬운 부작용을 처리하고 해당 오류를 응용 프로그램과 별도로 처리하는 모나드로 작업해야합니다.

3 Babakness Feb 28 2018 at 11:41

Task, futurize 및 순회 가능한 목록을 사용하여 간단하게 수행 할 수 있습니다.

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

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

설정 방법은 다음과 같습니다.

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)

원하는 코드를 구조화하는 또 다른 방법은 다음과 같습니다.

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

또는 훨씬 더 기능 지향적

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

그런 다음 부모 함수에서

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

인코딩에 더 많은 유연성을 원하신다면이 작업을 수행 할 수 있습니다 (재미있게 제안 된 Pipe Forward 연산자를 사용하고 있습니다 ).

import { curry, flip } from 'ramda'

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

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

추신-나는 콘솔에서이 코드를 시도하지 않았고, 약간의 오타가 있을지도 모른다 ... "직선적 인 자유형, 돔 꼭대기에서!" 90 년대 아이들이 말했듯이. :-피

3 Beau Mar 13 2019 at 06:31

현재 Array.forEach 프로토 타입 속성은 비동기 작업을 지원하지 않지만 필요에 맞게 자체 폴리 필을 만들 수 있습니다.

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

그리고 그게 다야! 이제 작업 이후에 정의 된 모든 배열에서 사용할 수있는 비동기 forEach 메서드가 있습니다.

테스트 해보자 ...

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

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

... 등등 :)

참고할 사항 :

  • iteratorFunction은 비동기 함수 또는 promise 여야합니다.
  • 이전에 생성 된 어레이 Array.prototype.<yourAsyncFunc> = <yourAsyncFunc>는이 기능을 사용할 수 없습니다.
3 PranavKAndro Nov 25 2019 at 03:31

오늘 저는 이에 대한 여러 솔루션을 발견했습니다. forEach 루프에서 비동기 대기 함수를 실행합니다. 래퍼를 만들어서 이런 일을 할 수 있습니다.

네이티브 forEach의 경우 내부적으로 작동하는 방법에 대한 자세한 설명과 비동기 함수 호출을 할 수없는 이유와 다양한 메서드에 대한 기타 세부 정보는 여기 링크에서 제공됩니다.

이를 수행 할 수있는 여러 가지 방법은 다음과 같습니다.

방법 1 : 래퍼 사용.

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

방법 2 : 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');
            }
        })
      });
    };
  }

사용법 :

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

방법 3 :

Promise.all 사용

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

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

방법 4 : 기존 for 루프 또는 최신 for 루프

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

을 사용할 수 Array.prototype.forEach있지만 async / await는 그렇게 호환되지 않습니다. 이는 비동기 콜백에서 반환 된 프라 미스가 해결 될 것으로 예상하지만 Array.prototype.forEach콜백 실행으로 인한 프라 미스를 해결하지 않기 때문입니다. 따라서 forEach를 사용할 수 있지만 약속 해결을 직접 처리해야합니다.

다음은 다음을 사용하여 각 파일을 연속적으로 읽고 인쇄하는 방법입니다. 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
}

다음은 Array.prototype.forEach파일 내용을 병렬로 인쇄 하는 방법 (여전히 사용 )입니다.

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

이것이 어떻게 잘못 될 수 있는지 확인하려면 메소드 끝에 console.log를 인쇄하십시오.

일반적으로 잘못 될 수있는 사항 :

  • 임의의 명령.
  • printFiles는 파일을 인쇄하기 전에 실행을 마칠 수 있습니다.
  • 성능 저하.

이것은 항상 잘못된 것은 아니지만 종종 표준 사용 사례에 있습니다.

일반적으로 forEach를 사용하면 마지막을 제외한 모든 결과가 나타납니다. 함수를 기다리지 않고 각 함수를 호출합니다. 즉, 함수가 완료 될 때까지 기다리지 않고 모든 함수가 시작되고 완료된다는 것을 의미합니다.

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

이것은 순서를 유지하고 함수가 조기에 반환되는 것을 방지하며 이론적으로 최적의 성능을 유지하는 네이티브 JS의 예입니다.

이것은 :

  • 병렬로 발생하도록 모든 파일 읽기를 시작합니다.
  • 기다릴 약속에 파일 이름을 매핑하는 맵을 사용하여 순서를 유지합니다.
  • 배열에 정의 된 순서대로 각 promise를 기다립니다.

이 솔루션을 사용하면 다른 파일을 먼저 사용할 수있을 때까지 기다릴 필요없이 사용할 수있는 즉시 첫 번째 파일이 표시됩니다.

또한 두 번째 파일 읽기가 시작되기 전에 첫 번째 파일이 완료 될 때까지 기다리지 않고 모든 파일을 동시에로드합니다.

이것과 원래 버전의 유일한 단점은 여러 읽기가 한 번에 시작되면 한 번에 발생할 수있는 오류가 더 많아서 오류를 처리하기가 더 어렵다는 것입니다.

한 번에 파일을 읽는 버전을 사용하면 더 이상 파일을 읽으려고 시간을 낭비하지 않고 실패시 중지됩니다. 정교한 취소 시스템을 사용하더라도 첫 번째 파일에서 실패하는 것을 피하는 것이 어려울 수 있지만 대부분의 다른 파일도 이미 읽습니다.

성능이 항상 예측 가능한 것은 아닙니다. 많은 시스템이 병렬 파일 읽기로 더 빠르지 만 일부는 순차를 선호합니다. 일부는 동적이며로드시 변경 될 수 있습니다. 대기 시간을 제공하는 최적화는 과도한 경합에서 항상 좋은 처리량을 생성하지는 않습니다.

이 예제에는 오류 처리도 없습니다. 무언가가 모두 성공적으로 표시되거나 전혀 표시되지 않도록 요구하는 경우 그렇게하지 않습니다.

각 단계에서 console.log 및 가짜 파일 읽기 솔루션 (대신 임의 지연)을 사용하여 심층 실험을하는 것이 좋습니다. 많은 솔루션이 단순한 사례에서 동일한 작업을 수행하는 것처럼 보이지만 모두 미묘한 차이를 가지고있어이를 파악하기 위해 추가 조사가 필요합니다.

이 모형을 사용하여 솔루션 간의 차이점을 구분하십시오.

(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

Antonio Val과 유사하게 p-iteration대체 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();

또는 async-af약속의 결과를 기록하는 정적 메서드 (log / logAF)가 있습니다.

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

그러나 라이브러리의 주요 장점은 비동기 메서드를 연결하여 다음과 같은 작업을 수행 할 수 있다는 것입니다.

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