sourcecode

각 루프에 비동기/대기 사용

copyscript 2022. 11. 17. 21:22
반응형

각 루프에 비동기/대기 사용

" " " " " " " " " " " " 를 사용하는 데 ?async/awaitin a a a a forEach 배열과 일련의 파일들을 루프하려고 하는데await각 파일의 내용에 대해 설명합니다.

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이런 고차적인 기능에서 문제가 없는지 여쭤보고 싶습니다.

암호는 작동하지만, 당신이 기대하는 대로 되지 않을 거예요.콜을 만, 「」는 「」라고 하는 것입니다.printFiles그 후 즉시 기능이 복귀합니다.

순서대로 읽기

파일을 순서대로 읽으려면 를 사용할 수 없습니다.그냥 현대식 사용for … of 이 에서 신신루루루 loop loop instead instead instead instead instead.await정상적으로 동작합니다.

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

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

병렬로 읽기

파일을 병렬로 읽으려면 를 사용할 수 없습니다.각각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)
  }));
}

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

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

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

사양 참조: proposal-async-iteration

심플화:

  for await (const results of array) {
    await longRunningTask()
  }
  console.log('I will wait')

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

Promise.all Array.prototype.map은 (이 순서를 )Promise해결되었습니다)를합니다.Array.prototype.reducePromise:

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

npm의 p-반복 모듈은 어레이 반복 방식을 구현하여 비동기/대기 방식으로 매우 쉽게 사용할 수 있도록 합니다.

케이스의 예:

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

1000단어 상당의 그림 - 순차적 접근만 가능


배경 : 어젯밤에도 비슷한 상황이었어요.foreach 인수로 비동기 함수를 사용했습니다.결과는 예측할 수 없었다.코드 테스트를 3회 실시했을 때, 2회 문제없이 동작해, 1회 실패.(뭔가 이상한데)

마침내 나는 정신을 차리고 몇 가지 스크래치 패드 테스트를 했다.

시나리오 1 - foreach의 비동기에서 얻을 수 있는 시퀀셜하지 않은 방법

여기에 이미지 설명 입력

const getPromise = (time) => { 
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(`Promise resolved for ${time}s`)
    }, time)
  })
}

const main = async () => {
  const myPromiseArray = [getPromise(1000), getPromise(500), getPromise(3000)]
  console.log('Before For Each Loop')

  myPromiseArray.forEach(async (element, index) => {
    let result = await element;
    console.log(result);
  })

  console.log('After For Each Loop')
}

main();

2 - 사용방법 2 -for - of의 @하는 바와 같이 루프합니다.

여기에 이미지 설명 입력

const getPromise = (time) => { 
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(`Promise resolved for ${time}s`)
    }, time)
  })
}

const main = async () => {
  const myPromiseArray = [getPromise(1000), getPromise(500), getPromise(3000)]
  console.log('Before For Each Loop')

  // AVOID USING THIS
  // myPromiseArray.forEach(async (element, index) => {
  //   let result = await element;
  //   console.log(result);
  // })

  // This works well
  for (const element of myPromiseArray) {
    let result = await element;
    console.log(result)
  }

  console.log('After For Each Loop')
}

main();

나처럼 조금 오래된 학교라면 클래식한 for loop을 사용하면 됩니다.그것도 효과가 있습니다.

const getPromise = (time) => { 
  return new Promise((resolve, reject) => {
    setTimeout(() => {
      resolve(`Promise resolved for ${time}s`)
    }, time)
  })
}

const main = async () => {
  const myPromiseArray = [getPromise(1000), getPromise(500), getPromise(3000)]
  console.log('Before For Each Loop')

  // AVOID USING THIS
  // myPromiseArray.forEach(async (element, index) => {
  //   let result = await element;
  //   console.log(result);
  // })

  // This works well too - the classic for loop :)
  for (let i = 0; i < myPromiseArray.length; i++) {
    const result = await myPromiseArray[i];
    console.log(result);
  }

  console.log('After For Each Loop')
}

main();

이게 도움이 됐으면 좋겠네요, 좋은 하루 되세요, 건배!

몇 가지 있습니다.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));
}

자신의 코드에 포함할 수 있지만 다른 사람에게 배포하는 라이브러리에는 포함하지 마십시오(글로벌 오염을 방지하기 위해).

@베르기는 이 특정 사건을 어떻게 적절히 처리할지에 대해 이미 답을 주었다.여기서 복제하지 않을게요.

사용방법의 차이점을 설명하고 싶습니다.forEach ★★★★★★★★★★★★★★★★★」for when when whenasync ★★★★★★★★★★★★★★★★★」await

?forEach

어떻게 하는지 forEachECMAScript 사양에 따르면 MDN은 폴리필로 사용할 수 있는 구현을 제공합니다.복사해서 댓글 삭제해서 여기에 붙여요.

Array.prototype.forEach = function (callback, thisArg) {
  if (this == null) { throw new TypeError('Array.prototype.forEach called on null or undefined'); }
  var T, k;
  var O = Object(this);
  var len = O.length >>> 0;
  if (typeof callback !== "function") { throw new TypeError(callback + ' is not a function'); }
  if (arguments.length > 1) { T = thisArg; }
  k = 0;
  while (k < len) {
    var kValue;
    if (k in O) {
      kValue = O[k];
      callback.call(T, kValue, k, O); // pay attention to this line
    }
    k++;
  }
};

코드로 돌아가서 함수로서 콜백을 추출합니다.

async function callback(file){
  const contents = await fs.readFile(file, 'utf8')
  console.log(contents)
}

기본적으로는 ★★★★★★★★★★★★★★★★★★★★★★★★★★★」callback로 선언되었기 때문에 약속을 반환한다.async .★★★★★★★★★★★★★★★★★★.forEach,callback콜백 자체가 약속을 반환하는 경우 Javascript 엔진은 해결되거나 거부될 때까지 기다리지 않습니다. "" " " " " 를 합니다.promise작업 큐에서 루프를 계속 실행합니다.

는 요?await fs.readFile(file, 'utf8')내 the의 callback

일 때callback js 이 일시 됩니다.fs.readFile(file, 'utf8')해결 또는 거부되어 처리 후 비동기 기능의 실행을 재개합니다. 그...contents를 하여 실제 를 저장하다fs.readFile 이에요.promiseconsole.log(contents) 내용을 합니다.Promise

★★★★for ... of가 있습니까

인 것을 쓸 때for of는 더 을 얻습니다.forEach 하겠습니다.printFiles.

async function printFiles () {
  const files = await getFilePaths() // Assume this works fine

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

」 「 」for '', '루프', ''await에서의 약속하다async기능을 이 일시 됩니다.await속이정정 정정정다다따라서 정해진 순서대로 파일을 하나씩 읽는다고 생각하면 됩니다.

순차적으로 실행

비동기 함수를 순차적으로 실행해야 하는 경우가 있습니다.예를 들어 배열에 저장된 새 레코드가 몇 개 있는데, 배열에 있는 첫 번째 레코드가 먼저 저장되고 그 다음 마지막 레코드가 저장될 때까지 순차적으로 저장되어야 합니다.

다음은 예를 제시하겠습니다.

const records = [1, 2, 3, 4];

async function saveRecord(record) {
  return new Promise((resolved, rejected) => {
    setTimeout(()=> {
      resolved(`record ${record} saved`)
    }, Math.random() * 500)
  });
}

async function forEachSaveRecords(records) {
  records.forEach(async (record) => {
    const res = await saveRecord(record);
    console.log(res);
  })
}

async function forofSaveRecords(records) {
  for (const record of records) {
    const res = await saveRecord(record);
    console.log(res);
  }
}
(async () => {
  console.log("=== for of save records ===")
  await forofSaveRecords(records)
  
  console.log("=== forEach save records ===")
  await forEachSaveRecords(records)
})()

용 i i i i를 쓴다.setTimeout데이터베이스에 레코드를 저장하는 프로세스를 시뮬레이트합니다.비동기적이고 랜덤한 시간이 소요됩니다.「」를 사용합니다.forEach 「」를 사용해 .for..of 됩니다.

이 솔루션은 메모리 최적화되어 있기 때문에 10,000개의 데이터 항목 및 요청에 대해 실행할 수 있습니다.여기에 있는 다른 솔루션 중 일부는 대규모 데이터 세트에서 서버가 크래시됩니다.

TypeScript의 경우:

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

사용방법?

await asyncForEach(receipts, async (eachItem) => {
    await ...
})
files.forEach(async (file) => {
    const contents = await fs.readFile(file, 'utf8')
})

forEach() 그과 ., . . . .가fs.readFile루프의 각에 호출된 "Da.", "Da.", "D" ()를 .fs.readFile작업을 완료해야 합니다.는 각 해결될 때문에 의 경우 루프는 을 종료합니다.아직 사용할 수 없는 값에 액세스하려고 할 수 있습니다.

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

는 「」에 해 주세요..map() does does 。async부터, syslogfs.readFile약속해 주세요.따라서 '''는promises객체의 , 、 Promise 、 음음음음음음 promise promise promise promise promise로 할 수 .Promise.all().

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

를 간단한 forEach()하지 않는 를 forEachmap "" 추가Promise.all(처음부터 끝까지.

예를 들어 다음과 같습니다.

await y.forEach(async (x) => {

로.

await Promise.all(y.map(async (x) => {

★★★★★★★)지막마

비동기 데이터를 시리얼 순서로 처리해, 코드에 종래의 맛을 더하는 몇개의 메서드를 파일에 넣는 것은 매우 간단하다.예를 들어 다음과 같습니다.

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

Bergi의 솔루션은 다음과 같은 경우에 잘 작동합니다.fs약속에 근거하고 있습니다.하시면 됩니다.bluebird,fs-extra ★★★★★★★★★★★★★★★★★」fs-promise이걸 위해서.

단, 노드 네이티브 솔루션fs libary 음 음 음 음 음 다 다 다 다 lib lib lib lib lib

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

루프에서 비동기 메서드를 호출하는 것은 좋지 않습니다.이는 비동기 조작 전체가 완료될 때까지 각 루프 반복이 지연되기 때문입니다.그것은 별로 성과가 없다., 할 수 있습니다.async/await.

은 모든 한 , 그 '약속하다'를 사용해야 .Promise.all()그렇지 않으면 이전 작업이 완료될 때까지 각 연속 작업이 시작되지 않습니다.

따라서 코드는 다음과 같이 리팩터링할 수 있다.

const printFiles = async () => {
  const files = await getFilePaths();
  const results = [];
  files.forEach((file) => {
    results.push(fs.readFile(file, 'utf8'));
  });
  const contents = await Promise.all(results);
  console.log(contents);
}

한 가지 중요한 경고는 다음과 같습니다.await + for .. of 및 「」forEach + async방법은 실제로 다른 영향을 미칩니다.

있다await 짜 a a for은 모든 실행되도록 .loop은 비동기 콜을 하나씩 합니다. ★★★★★★★★★★★★★★★★.forEach + asyncway는 모든 약속을 동시에 실행합니다.이것은 고속이지만 경우에 따라서는 오버로드가 될 수 있습니다(데이터베이스 쿼리를 실행하거나 볼륨 제한이 있는 웹 서비스를 방문하거나 한 번에 100,000 콜을 실행하고 싶지 않은 경우).

이 경우에도 하실 수 있습니다.reduce + promiselugent)는 (less lugent)async/await파일을 차례차례 읽어내도록 해 주세요.

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

또는 EachAsync를 작성하면 도움이 되지만 기본적으로는 루프 기반에도 동일하게 사용할 수 있습니다.

Array.prototype.forEachAsync = async function(cb){
    for(let x of this){
        await cb(x);
    }
}

원래 답에 더해서

  • 원래 답안의 병렬 읽기 구문은 때때로 혼란스럽고 읽기 어렵습니다. 다른 접근법으로 쓸 수 있을지도 모릅니다.
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 루프가 작동합니다.
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);
  }
}

그러나 위의 두 솔루션 모두 Antonio는 적은 코드로 작업을 수행합니다.다음은 데이터베이스, 여러 가지 다른 하위 참조에서 데이터를 해결한 후 어레이에 모두 푸시하여 약속으로 해결할 수 있는 방법을 보여 줍니다.

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

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

PS

readFilesQueue printFiles의 되다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)

변수 이름은 의미론에 따라 달라집니다.함수(다른 함수에 의해 호출될 수 있는 함수)를 전달하고 응용 프로그램의 로직의 초기 블록을 포함하는 메모리 상의 포인터를 수신합니다.

하지만 모듈이 아닌 경우 로직을 내보내야 합니까?

함수를 비동기 함수로 랩합니다.

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

아니면 변수 이름을 바꾸든지...


*부작용으로 IO와 같이 애플리케이션의 상태/동작을 변경하거나 버그를 침입할 수 있는 애플리케이션의 모든 콜랙테랄 효과를 관리합니다.

**"pure"에 의해, 순수하지 않은 함수와 코드가 순수 버전으로 수렴될 수 있기 때문에, 콘솔 출력이 없을 때는 데이터 조작만 가능합니다.

이와는 별도로, 순수하게 하기 위해서는, 에러가 발생하기 쉬운, 애플리케이션과는 별도로, 부작용을 처리하는 모나드를 사용할 필요가 있습니다.

오늘 저는 이 문제에 대한 여러 가지 해결책을 찾았습니다.비동기 대기 기능을 실행하면 각 루프의 기능이 실행됩니다.포장지를 주변에 구축함으로써 이를 실현할 수 있습니다.

각 네이티브의 내부 기능 및 비동기 함수 호출을 할 수 없는 이유 및 다양한 메서드에 대한 자세한 내용은 다음 링크를 참조하십시오.

여러 가지 방법으로 수행할 수 있으며 다음과 같습니다.

방법 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.protype의 일반적인 기능과 동일한 기능을 사용합니다.

Array.protype.각 Async.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 사용모든.

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

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

방법 4: 루프의 경우 기존 또는 루프의 경우 모던

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

하시면 됩니다.Array.prototype.forEach아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아, 아.되지만 "이러한 약속"이입니다.Array.prototype.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)
}

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

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

바로 그거야!이제 각 메서드에 대해 비동기식을 사용할 수 있게 되었습니다.이러한 메서드는 이후부터 작업에 대해 정의된 어레이에서 모두 사용할 수 있습니다.

시험해 보자...

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

지도와 같은 다른 어레이 기능에도 동일한 작업을 수행할 수 있습니다.

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

...등등:)

주의사항:

  • 반복기함수는 비동기 함수 또는 약속이어야 합니다.
  • " " " 에 작성된 Array.prototype.<yourAsyncFunc> = <yourAsyncFunc>에서는 이 할 수 .

이 문제가 발생하는 원인을 확인하려면 메서드의 마지막에 console.log를 인쇄합니다.

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

  • 임의의 순서
  • printFiles는 파일을 인쇄하기 전에 실행을 완료할 수 있습니다.
  • 퍼포먼스가 나쁘다.

이는 항상 잘못된 것은 아니지만 표준 사용 사례에서 자주 사용됩니다.

일반적으로 for Each를 사용하면 마지막을 제외한 모든 것이 됩니다.함수를 기다리지 않고 각 함수를 호출합니다. 즉, 모든 함수가 시작되도록 지시하고 함수가 완료될 때까지 기다리지 않고 종료됩니다.

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에서 질서를 유지하고, 함수의 조기 복귀를 방지하며, 이론적으로 최적의 성능을 유지하는 예입니다.

이것은 다음과 같습니다.

  • 모든 파일 읽기를 병렬로 시작합니다.
  • 맵을 사용하여 파일 이름을 대기 약속에 매핑하여 순서를 유지합니다.
  • 어레이에서 정의된 순서대로 각 약속을 기다립니다.

이 솔루션에서는 첫 번째 파일이 사용 가능하게 되는 즉시 다른 파일이 사용 가능하게 될 때까지 기다릴 필요가 없습니다.

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

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

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

퍼포먼스를 항상 예측할 수 있는 것은 아닙니다.많은 시스템이 병렬 파일 읽기를 통해 더 빨라지지만 일부 시스템은 순차적 파일 읽기를 선호합니다.일부는 동적이고 부하가 걸린 상태에서 이동할 수 있지만, 레이텐시를 제공하는 최적화는 심한 경합에서 항상 우수한 throughput을 산출하는 것은 아닙니다.

이 예에서는 에러 처리도 행해지지 않습니다.만약 어떤 것이 그것들을 모두 성공적으로 보여주거나 전혀 보여주지 않으면, 그것은 그렇게 하지 않을 것이다.

상세한 실험은 각 단계에서 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}`);
})();

태스크, 미래화 및 트래버블리스트를 사용하면 간단하게 다음 작업을 수행할 수 있습니다.

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

부호화의 유연성이 필요한 경우는, 이것을 실시합니다(재미삼아, 제안된 파이프 포워드 연산자를 사용하고 있습니다).

import { curry, flip } from 'ramda'

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

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

추신 - 콘솔에서 이 코드를 시도하지 않았습니다.오타가 있을 수 있습니다."프리스타일, 돔 꼭대기에서 떨어져!" 라고 90년대 아이들이 말하는 것처럼. :-p

다음은 각 루프에서 비동기 사용을 위한 좋은 예입니다.

비동기 For 각각을 작성하다

async function asyncForEach(array, callback) {  
    for (let index = 0; index < array.length; index++) {
        await callback(array[index], index, array)
    }
}

이렇게 쓸 수 있어요.

await asyncForEach(array, async function(item,index,array){
     //await here
   }
)

OP의 최초 질문

각 루프에서 비동기/대기 사용 시 문제가 있습니까?...

@Bergi가 선택한 답변에서 어느 정도 다루어졌습니다.이 답변은 연속 처리와 병렬 처리 방법을 보여 줍니다.그러나 병렬 처리에서 지적된 다른 문제가 있습니다.

  1. Order -- @charvey는 다음과 같이 기술합니다.

예를 들어, 매우 작은 파일이 매우 큰 파일보다 먼저 읽혀진 경우, 작은 파일이 파일 배열의 큰 파일 뒤에 있더라도 먼저 기록됩니다.

  1. 한 번에 너무 많은 파일을 열 수 있음 - 다른 답변 아래에 있는 Bergi의 코멘트

동시에 읽기 위해 수천 개의 파일을 동시에 여는 것도 좋지 않습니다.순차적 접근, 병렬 접근 또는 혼합 접근 중 어느 것이 더 나은지 항상 평가해야 합니다.

그럼 서드파티 라이브러리를 사용하지 않고 간결하고 간단한 실제 코드를 보여 주는 이러한 문제에 대해 살펴보겠습니다.쉽게 자르고, 붙이고, 수정할 수 있는 것.

병렬로 읽거나(한 번에), 직렬로 인쇄하거나(파일당 가능한 한 빨리).

가장 간단한 개선은 @Bergi의 답변과 같이 완전한 병렬 처리를 수행하지만, 순서를 유지하면서 각 파일이 가능한 빨리 인쇄되도록 작은 변경을 가하는 것입니다.

async function printFiles2() {
  const readProms = (await getFilePaths()).map((file) =>
    fs.readFile(file, "utf8")
  );
  await Promise.all([
    await Promise.all(readProms),                      // branch 1
    (async () => {                                     // branch 2
      for (const p of readProms) console.log(await p);
    })(),
  ]);
}

위에서는 2개의 다른 분기가 동시에 실행됩니다.

  • 분기 1: 병렬로 동시에 읽습니다.
  • 브랜치 2: 시리얼로 읽어서 순서를 강제화하고 있습니다만, 필요이상으로 대기하고 있지 않습니다.

그건 쉬웠어요.

동시성 제한과 병행하여 읽기, 직렬로 인쇄(파일당 가능한 한 빨리).

"하다, 하다, 하다" 입니다.N을 사용법
(최소한 COVID 기간 동안) 한 번에 많은 고객만 출입할 수 있는 가게처럼.

먼저 도우미 기능을 소개합니다.

function bootablePromise(kickMe: () => Promise<any>) {
  let resolve: (value: unknown) => void = () => {};
  const promise = new Promise((res) => { resolve = res; });
  const boot = () => { resolve(kickMe()); };
  return { promise, boot };
}

★★★bootablePromise(kickMe:() => Promise<any>)kickMe로서 (이는 (이 경우)readFile 바로 하는 것은 .) 라고, 라고 하다.

bootablePromise합니다.

  • promise입입 of Promise
  • boot 함수 「」의()=>void

promise의 두

  1. 일을 시작하기로 약속하는 것
  2. 약속을 지키면 이미 시작한 일을 완수할 수 있다.

promise「」가 되었을 때, 스테이트로 합니다.boot()출됩니니다다

bootablePromise요.printFiles

async function printFiles4() {
  const files = await getFilePaths();
  const boots: (() => void)[] = [];
  const set: Set<Promise<{ pidx: number }>> = new Set<Promise<any>>();
  const bootableProms = files.map((file,pidx) => {
    const { promise, boot } = bootablePromise(() => fs.readFile(file, "utf8"));
    boots.push(boot);
    set.add(promise.then(() => ({ pidx })));
    return promise;
  });
  const concurLimit = 2;
  await Promise.all([
    (async () => {                                       // branch 1
      let idx = 0;
      boots.slice(0, concurLimit).forEach((b) => { b(); idx++; });
      while (idx<boots.length) {
        const { pidx } = await Promise.race([...set]);
        set.delete([...set][pidx]);
        boots[idx++]();
      }
    })(),
    (async () => {                                       // branch 2
      for (const p of bootableProms) console.log(await p);
    })(),
  ]);
}

이전과 마찬가지로 두 개의 지점이 있다.

  • 브랜치 1: 동시 실행 및 처리용.
  • 브랜치 2: 인쇄용

그 .concurLimit을 사용법

중요한 변수는 다음과 같습니다.

  • boots에 대한 대응하는 이행 약속을 강제하기 위해 호출하는 함수의 배열.1번으로 하다
  • set: 후 할 수 랜덤 액세스 용기에 약속이 들어 있어 이행 후 쉽게 제거할 수 있습니다.됩니다.
  • bootableProms: 에 : smae의 set단, 어레이는 세트가 아닌 어레이이며 어레이는 변경되지 않습니다.2번입니다.

달리다fs.readFile다음과 같은 시간이 소요됩니다(시간 대 밀리초 단위).

const timeTable = {
  "1": 600,
  "2": 500,
  "3": 400,
  "4": 300,
  "5": 200,
  "6": 100,
};

이와 같은 테스트 실행 시간이 표시되며 동시 실행이 작동 중임을 나타냅니다.

[1]0--0.601
[2]0--0.502
[3]0.503--0.904
[4]0.608--0.908
[5]0.905--1.105
[6]0.905--1.005

활자놀이터 샌드박스에서 실행 파일로 사용 가능

다른 답변에서도 언급했듯이 병렬이 아닌 순차적으로 실행되기를 원할 수 있습니다.즉, 첫 번째 파일에 대해 실행하고 완료될 때까지 기다린 후 두 번째 파일에 대해 실행합니다.그런 일은 일어나지 않을 거야.

왜 이런 일이 일어나지 않는지 설명하는 것이 중요하다고 생각합니다.

하면 좋을지 해 보세요.forEach과가있있 있있있다다 출처는 찾을 수 없지만, 다음과 같이 작동한다고 생각합니다.

const forEach = (arr, cb) => {
  for (let i = 0; i < arr.length; i++) {
    cb(arr[i]);
  }
};

이제 다음과 같은 작업을 수행할 때 어떤 일이 발생하는지 생각해 보십시오.

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

forEach의 »for호출하고 " " loop loop loop loop loop loop loop loopcb(arr[i])은 「 「」가 되어 버립니다만logFile(file) . 。logFile에 「」가 .await안에 그 안에 있을 거예요.for는 이 루프를 .await으로 넘어가기 i++

아니, 아닐 것이다. 것이 .await효과가 있습니다.문서:

wait는 실행 플로우를 분할하여 비동기 함수의 호출자가 실행을 재개할 수 있도록 합니다.대기 상태가 비동기 기능의 계속을 지연시킨 후 후속 문의 실행이 이루어집니다.이 wait가 함수 실행에 의해 실행되는 마지막 표현일 경우, 함수의 호출자에게 대기 기능의 완료를 위한 보류 중인 약속을 반환하고 해당 호출자의 실행을 재개함으로써 계속됩니다.

것이 그 "b":

const delay = (ms) => {
  return new Promise((resolve) => {
    setTimeout(resolve, ms);
  });
};

const logNumbers = async () => {
  console.log(1);
  await delay(2000);
  console.log(2);
  await delay(2000);
  console.log(3);
};

const main = () => {
  console.log("a");
  logNumbers();
  console.log("b");
};

main();

forEach,forEach~와 main ★★★★★★★★★★★★★★★★★」logFile~와 logNumbersmain because wonlogNumbers 정도 하다await및 「」입니다.forEach because wonlogFile 정도 하다await

Antonio Val과 마찬가지로 npm 모듈은 다음과 같습니다.

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

또는 정적 방식(log/log)을 사용합니다.AF)는, 약속의 결과를 기록합니다.

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

모든 요소를 동시에 반복하는 경우:

async function asyncForEach(arr, fn) {
  await Promise.all(arr.map(fn));
}

모든 요소에 대해 동시에 반복하는 경우(매핑 기능에 부작용이 있거나 모든 어레이 요소에 대해 동시에 매퍼를 실행하는 경우 등)는 리소스가 너무 많이 소요됩니다.

옵션 A: 약속

function asyncForEachStrict(arr, fn) {
  return new Promise((resolve) => {
    arr.reduce(
      (promise, cur, idx) => promise
        .then(() => fn(cur, idx, arr)),
      Promise.resolve(),
    ).then(() => resolve());
  });
}

옵션 B: 비동기/대기

async function asyncForEachStrict(arr, fn) {
  for (let idx = 0; idx < arr.length; idx += 1) {
    const cur = arr[idx];

    await fn(cur, idx, arr);
  }
}

비동기/대기(IE11, 오래된 패커 등)를 사용할 수 없는 경우는, 이 재귀 함수를 사용해 주세요.나는 사용했다fetch비동기 호출로 사용할 수 있지만 약속을 반환하는 모든 함수를 사용할 수 있습니다.

var urlsToGet = ['https://google.com', 'https://yahoo.com'];

fetchOneAtATime(urlsToGet);

function fetchOneAtATime(urls) {
    if (urls.length === 0) {
        return;
    }
    fetch(urls[0]).finally(() => fetchOneAtATime(urls.slice(1)));
}

이것은 OP 요구대로 비동기/대기 기능을 사용하지 않으며 NodeJ를 사용하는 백엔드에 있는 경우에만 작동합니다.OP의 예에서는 파일 내용을 읽고, 일반적으로 백엔드에서 파일 읽기를 하기 때문에 도움이 되는 사람도 있습니다.

완전 비동기 및 논블로킹:

const fs = require("fs")
const async = require("async")

const obj = {dev: "/dev.json", test: "/test.json", prod: "/prod.json"}
const configs = {}

async.forEachOf(obj, (value, key, callback) => {
    fs.readFile(__dirname + value, "utf8", (err, data) => {
        if (err) return callback(err)
        try {
            configs[key] = JSON.parse(data);
        } catch (e) {
            return callback(e)
        }
        callback()
    });
}, err => {
    if (err) console.error(err.message)
    // configs is now a map of JSON data
    doSomethingWith(configs)
})

언급URL : https://stackoverflow.com/questions/37576685/using-async-await-with-a-foreach-loop

반응형