개발자공부일기
Promise와 forEach의 동작 차이 본문
오늘 팀프로젝트에서 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 자체는 비동기 처리가 끝나는 것을 기다리지 않습니다. 따라서 다음과 같은 이유로 적합하지 않습니다:
- 비동기 함수 반환값 무시 forEach는 콜백 함수가 반환하는 값을 무시합니다. 예를 들어, 반환된 Promise를 처리하지 않습니다.
- const numbers = [1, 2, 3]; numbers.forEach(async (num) => { await new Promise((resolve) => setTimeout(() => resolve(), 1000)); console.log(num); }); console.log('Done'); // 'Done'이 먼저 출력됨
- 비동기 함수 실행 순서 보장 불가 비동기 작업이 병렬로 실행되며 완료 순서가 보장되지 않습니다.
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 |