Notice
Recent Posts
Recent Comments
Link
«   2025/01   »
1 2 3 4
5 6 7 8 9 10 11
12 13 14 15 16 17 18
19 20 21 22 23 24 25
26 27 28 29 30 31
Tags
more
Archives
Today
Total
관리 메뉴

개발자공부일기

Promise와 forEach의 동작 차이 본문

TIL(Today I Learned)

Promise와 forEach의 동작 차이

JavaCPP 2024. 12. 31. 21:17

오늘 팀프로젝트에서 forEach안에서 Promise를 사용했는데 작동을 안해서 알아봤다.

 

Promise와 forEach의 동작 차이


1. forEach의 동작 방식

forEach는 배열의 각 요소를 동기적으로 순회하며 콜백 함수를 실행합니다.
즉, 배열의 모든 요소를 비동기 작업 여부와 관계없이 바로 처리합니다.

기본 동작

const numbers = [1, 2, 3];

numbers.forEach((num) => {
  console.log(num); // 동기적으로 즉시 실행
});
console.log('Done'); // forEach가 끝나고 실행

출력:

1
2
3
Done

2. forEach와 비동기 함수

forEach에서 콜백 함수가 async일 경우, 콜백 함수가 반환하는 Promise는 무시됩니다.
forEach 자체는 Promise를 기다리지 않고 다음 요소로 넘어갑니다.

비동기 작업 예제

const numbers = [1, 2, 3];

numbers.forEach(async (num) => {
  const result = await new Promise((resolve) =>
    setTimeout(() => resolve(num * 2), 1000)
  );
  console.log(result); // 비동기적으로 실행
});

console.log('Done'); // forEach 완료 후 실행

출력:

Done
2
4
6
  • forEach는 비동기 작업을 기다리지 않으므로 Done이 먼저 출력됩니다.
  • 이후 각 비동기 작업이 완료되면서 2, 4, 6이 출력됩니다.

3. for...of와 await

for...of는 async 함수와 잘 작동하며, 순차적으로 비동기 작업을 처리합니다.
즉, 각 작업이 완료될 때까지 기다렸다가 다음 작업으로 진행합니다.

순차적 처리 예제

const numbers = [1, 2, 3];

(async () => {
  for (const num of numbers) {
    const result = await new Promise((resolve) =>
      setTimeout(() => resolve(num * 2), 1000)
    );
    console.log(result); // 순차적으로 실행
  }
  console.log('Done');
})();

출력:

2
4
6
Done
  • await는 각 Promise가 완료될 때까지 기다립니다.
  • 결과적으로 작업이 순차적으로 실행됩니다.

4. Promise.all과 map

Promise.all은 비동기 작업을 병렬로 실행하고, 모든 작업이 완료된 후 결과를 반환합니다.
map을 사용해 각 요소에 대해 Promise 배열을 생성합니다.

병렬 처리 예제

const numbers = [1, 2, 3];

const promiseArray = numbers.map((num) =>
  new Promise((resolve) => setTimeout(() => resolve(num * 2), 1000))
);

Promise.all(promiseArray).then((results) => {
  results.forEach((result) => console.log(result));
  console.log('Done');
});

출력:

2
4
6
Done
  • 모든 비동기 작업이 동시에 시작됩니다.
  • 1초 후에 모든 작업이 완료되며 결과가 출력됩니다.

5. forEach와 비동기 작업의 근본적 문제

forEach는 Promise를 다룰 수 있는 메서드가 아닙니다.
forEach 자체는 비동기 처리가 끝나는 것을 기다리지 않습니다. 따라서 다음과 같은 이유로 적합하지 않습니다:

  1. 비동기 함수 반환값 무시 forEach는 콜백 함수가 반환하는 값을 무시합니다. 예를 들어, 반환된 Promise를 처리하지 않습니다.
  2. const numbers = [1, 2, 3]; numbers.forEach(async (num) => { await new Promise((resolve) => setTimeout(() => resolve(), 1000)); console.log(num); }); console.log('Done'); // 'Done'이 먼저 출력됨
  3. 비동기 함수 실행 순서 보장 불가 비동기 작업이 병렬로 실행되며 완료 순서가 보장되지 않습니다.

6. 대안 비교

방법 특징 적합한 상황

forEach 동기적, 비동기 작업을 기다리지 않음 비동기 작업이 필요 없는 경우
for...of 비동기 작업을 순차적으로 실행 각 작업의 순서를 보장해야 할 때
Promise.all 비동기 작업을 병렬로 실행 모든 작업을 병렬로 처리할 때

7. 실제 상황 예제

문제 상황:

서버에서 사용자 데이터를 가져오고 처리해야 한다고 가정해 봅시다.

const users = [1, 2, 3];

users.forEach(async (id) => {
  const data = await fetchUserData(id); // 서버 요청
  console.log(data); // 사용자 데이터 출력
});

console.log('Done');
  • 문제: Done이 사용자 데이터 출력보다 먼저 실행됩니다.
  • 해결: for...of 또는 Promise.all을 사용합니다.

해결 1: for...of로 순차적 처리

(async () => {
  for (const id of users) {
    const data = await fetchUserData(id); // 순차 처리
    console.log(data);
  }
  console.log('Done');
})();

해결 2: Promise.all로 병렬 처리

const promises = users.map((id) => fetchUserData(id));

Promise.all(promises).then((results) => {
  results.forEach((data) => console.log(data));
  console.log('Done');
});

 

  • forEach는 비동기 작업을 기다리지 않으므로 비동기 작업에는 적합하지 않습니다.
  • 작업 순서를 보장해야 한다면 for...of와 await를 사용하세요.
  • 병렬로 작업을 처리하려면 Promise.all을 사용하세요.

'TIL(Today I Learned)' 카테고리의 다른 글

컴퓨터구조 - CPU  (0) 2025.01.06
버퍼와 스트림  (0) 2025.01.02
I/O(Input/Output) 최적화  (0) 2024.12.30
OSI 응용 계층  (0) 2024.12.26
운영체제 (Operating System)란?  (0) 2024.12.24