///

JavaScript async forEach Capsule

forEach(async fn) 는 promise 를 무시 한다. printFiles() 이 즉시 반환 + 내부 async 가 백그라운드로 떠있음. 순차 실행이 필요하면 for…of , 병렬은 Promise.all(arr.map(async …)) .

///

kind: capsule status: active visibility: private license: CC-BY-SA-4.0 summary: async/await 와 forEach 함께 쓰면 안 되는 이유 — forEach 는 반환 promise 를 버림. 순차는 for…of, 병렬은 Promise.all(map). tags: - javascript - async - promise - iteration - capsule


JavaScript async forEach Capsule

Summary#

forEach(async fn)promise 를 무시한다. printFiles() 이 즉시 반환 + 내부 async 가 백그라운드로 떠있음. 순차 실행이 필요하면 for…of, 병렬은 Promise.all(arr.map(async …)).

Claim#

❌ 의도대로 동작하지 않음#

async function printFiles () {
  const files = await getFilePaths();
  files.forEach(async (file) => {
    const contents = await fs.readFile(file, 'utf8');
    console.log(contents);
  });
  // ↑ 여기서 함수가 이미 반환됨
  // forEach 는 async 콜백이 반환한 promise 들을 전부 버림
}

✅ 순차 — for…of#

async function printFiles () {
  const files = await getFilePaths();
  for (const file of files) {
    const contents = await fs.readFile(file, 'utf8');
    console.log(contents);
  }
}
  • 각 반복에서 await 이 정확히 작동
  • 파일을 순서대로 읽음 → 읽기 간 의존성 있을 때 필수

✅ 병렬 — Promise.all + map#

async function printFiles () {
  const files = await getFilePaths();
  await Promise.all(files.map(async (file) => {
    const contents = await fs.readFile(file, 'utf8');
    console.log(contents);
  }));
}
  • .map(async …) 은 promise 배열 생성
  • Promise.all 이 전부 await
  • 각 파일 I/O 가 동시에 진행 → 대량 작업에서 극적 속도 향상

제어된 병렬성 (N개씩)#

import pLimit from 'p-limit';
const limit = pLimit(5);           // 최대 5개 동시
const results = await Promise.all(
  files.map(file => limit(() => fs.readFile(file, 'utf8')))
);

수백~수천 파일 동시 open 시 파일 핸들 고갈 방지.

왜 forEach 가 문제인가#

  • Array.prototype.forEach 시그니처는 (callback) => void
  • 콜백이 반환하는 값(promise 포함)을 버린다
  • → async/await 의 semantics 와 근본적으로 안 맞음

Scope#

  • ES2017+ async/await 표준.
  • 동일 원리는 map 반환값 무시 (.then 안 붙이고 버림) 등 모든 고차 함수에 적용.

Caveats#

  • Promise.all하나가 reject 되면 즉시 rejected → 부분 실패 허용 필요하면 Promise.allSettled.
  • for…of 안에서도 await 없이 동기식 처리 원하면 그냥 for 루프.
  • hot promise vs cold: .map(async …)즉시 promise 시작. 직렬화 원하면 for…of 외에 선택지 없음.
  • Node.js 의 fs/promises 사용. CommonJS fsreadFile 은 callback 기반.

Source#

Sagwan Revalidation 2026-04-18T22:57:32Z#

  • verdict: ok
  • note: forEach/for…of/Promise.all 패턴 및 p-limit 권장안 모두 2026년 현재도 유효한 JavaScript 표준 실천법임.