JS

Promise.all() 사용하기

2025. 4. 25. 14:50

Promise.all()

Promise를 활용하는 여러 개의 비동기 작업들을 병렬적으로 처리하고 싶을 때 쓰기 좋은 메서드이다.

지난번에 효율적으로 비동기 작업하는 방법으로 소개되었던 'for문 바깥에 쓰기' 예시 코드를 그대로 가져왔다.

async function getEmployee(id) {
  const response = await fetch('https://url.com/api/employees/${id}');
  const data = await response.json();
  console.log(data);
}

for (let i=1; i<11; i++) {
  getEmployee(i);
}

 

그런데 만약, 비동기 작업 후 받은 결과값을 콘솔 로그로 찍고 싶은 게 아니라 return해서 함수 밖에서 쓰고 싶다면?

async function getEmployee(id) {
  const response = await fetch('https://url.com/api/employees/${id}');
  const data = await response.json();
  // console.log(data);
  return data;
}

for (let i=1; i<11; i++) {
  // getEmployee(i);
  const employee = await getEmployee(i);
}
  • 프로미스 형태로 리턴되는 'data'의 결과값을 얻기 위해 getEmployee(i) 앞에 'await'을 붙이고, 
  • 그 결과값을 새 변수 'const employee'에 담아서 쓰면 된다.

하지만 이렇게 하면 await으로 결과값을 얻는 과정에서 또 시간이 걸리게 된다.

어차피 일괄적으로 프로미스를 풀어야 하는 거라면, 이때 Promise.all()을 사용할 수 있는 것이다.

 

 

적용

먼저 for문 안의 await을 해제하고, 프로미스들을 전부 하나의 배열에 담는다.

const promises = [];

for (let i=1; i<11; i++) {
  promises.push(getEmployee(i));  // 풀리지 않은 프로미스인 채로 배열에 모두 저장
}

이렇게 하면 await을 하지 않기 때문에 리퀘스트를 거의 병렬적으로(동시에) 보내게 된다.

이때 promises 배열에 추가되는 것은 결과값이 아니라 '프로미스 객체'이다.

여기서 바로 all()을 사용해주면 된다.

 

const promises = [];

for (let i=1; i<11; i++) {
  promises.push(getEmployee(i));  // 풀리지 않은 프로미스인 채로 배열에 모두 저장
}

Promise.all(promises);

Promise.all()을 통해 받은 리턴도 역시 '프로미스'이므로, 결과값까지 받고 싶으면 마찬가지로 await을 통해 풀어줘야 한다.

(리퀘스트 요청 결과 리스폰스 받기에 모두 성공하면 fulfilled, 하나라도 실패하면 rejected 프로미스가 반환됨)

 

// 최종 코드
const promises = [];

for (let i=1; i<11; i++) {
  promises.push(getEmployee(i));  
}

const employees = await Promise.all(promises);
console.log(employees);  // 직원 정보 결과값 출력

 

 

try catch 추가

try catch로 promise.all()의 에러도 핸들링해보자.

이때는 try 블록 안에 프로미스를 담는 작업이 들어가므로, 프로미스 담는 배열을 try catch 블록 바깥에 전역으로 설정해야 한다.

const promises = [];

for (let i=1; i<11; i++) {
  promises.push(getEmployee(i));  
}

let employees;

try {
  employees = await Promise.all(promises);
} catch (error) {
  console.log(error);
}

console.log(employees);

 

 

응용 문제

두개의 api에 동시에 리퀘스트를 보내고, 직원 데이터는 employees 변수에, 메뉴 데이터는 menus 변수에 저장해 주세요.

// asyncFunctions.js
export async function getEmployees() {
  try {
    const response = await fetch('https://learn.codeit.kr/api/employees');
    const employees = await response.json();
    return employees;
  } catch (error) {
    console.log('데이터를 가져오지 못했습니다 :(');
    return null;
  }
}

export async function getMenus() {
  try {
    const response = await fetch('https://learn.codeit.kr/api/menus');
    const menus = await response.json();
    return menus;
  } catch (error) {
    console.log('데이터를 가져오지 못했습니다 :(');
    return null;
  }
}
import { getEmployees, getMenus } from './asyncFunctions.js';

// 코드 작성

// 테스트 코드
console.log('직원 데이터:');
console.log(employees);
console.log('메뉴 데이터:');
console.log(menus);

 

[답]

더보기
import { getEmployees, getMenus } from './asyncFunctions.js';

const employeesPromises = getEmployees();
const menusPromises = getMenus();

const result = await Promise.all([employeesPromises, menusPromises]);
const employees = result[0];
const menus = result[1];

// 테스트 코드
console.log('직원 데이터:');
console.log(employees);
console.log('메뉴 데이터:');
console.log(menus);

 

[해설]

Promise.all()은 인자를 '배열' 형태로만 받는다.

그래서 await을 쓰지 않고 프로미스들만 받은 객체들인 employeesPromises, menusPromises를 all()에 인자로 담을 때,

'배열'로 감싸서 담지 않으면 객체를 담았다고 에러가 난다.

 

따라서 Promise.all([ employeesPromises, menusPromises])와 같은 형태로 all()을 사용하고,

마찬가지로 리턴되는 프로미스 형태를 결과값으로 풀기 위해 await을 붙여 'result'라는 변수에 담는다.

이때 result 변수에는 employeesPromises의 결과값, menusPromises의 결과값이 각각 0, 1의 배열 인덱스로 나뉘어서 저장된다.

따라서 이 결과값들을 직원/메뉴 데이터로 각각 나눠서 employees, menus 변수에 저장하려면 배열 인덱스로 접근하면 된다.

=> const employees = result[0]; 

=> const menus = result[1];

 

그리고 result부터 menus까지 총 3줄의 코드를 1줄로 줄일 수가 있는데, 바로 'Destructuring(구조 분해 할당)'을 이용하면 된다.

 

따라서 모범 답안 코드는 아래와 같다.

import { getEmployees, getMenus } from './asyncFunctions.js';

const employeesPromises = getEmployees();
const menusPromises = getMenus();

const [employees, menus] = await Promise.all([employeesPromises, menusPromises]);

// 테스트 코드
console.log('직원 데이터:');
console.log(employees);
console.log('메뉴 데이터:');
console.log(menus);

이제 구조 분해 할당에 대해 감이 좀 잡히나? (나 자신에게 하는 말)

 

 

Promise.all() 실행 결과, rejected된 객체가 여러개일 때?

Promise.all()은 등록된 promise중에 하나라도 오류가 발생하면

  • 거부된 첫 번째 promise 결과를 반환하고
  • 다른 promise 결과는 무시한다.

그래서 만약 promise들의 성공 여부를 각각 배열로 받고 싶다면, Promise.all()이 아닌 Promise.allSettled()을 사용하면 된다.

allSettled()는 각 promise의 결과를 배열로 반환한다.

'JS' 카테고리의 다른 글

함수 파라미터를 중괄호로 감싸서 받을 때와 그냥 받을 때의 차이  (0) 2025.05.10
자주 사용되는 JavaScript Web API들  (0) 2025.05.10
효율적인 비동기 코드 (feat. for문)  (0) 2025.04.25
default export vs named export  (0) 2025.04.22
some()과 every()  (0) 2025.04.22
'JS' 카테고리의 다른 글
  • 함수 파라미터를 중괄호로 감싸서 받을 때와 그냥 받을 때의 차이
  • 자주 사용되는 JavaScript Web API들
  • 효율적인 비동기 코드 (feat. for문)
  • default export vs named export
쥬피썬더의노예
쥬피썬더의노예
오히려 좋아
  • 쥬피썬더의노예
    d.log
    쥬피썬더의노예
    글쓰기 관리
    • 분류 전체보기 (112)
      • JS (37)
      • TS (3)
      • WEB (10)
      • React.js (20)
      • Next.js (4)
      • tanstack query (2)
      • Node.js (2)
      • HTML (5)
      • CSS (13)
      • CS (1)
      • 에이전트 (1)
      • Git (4)
      • JAVA (0)
      • SQL (0)
      • db (0)
      • GSAP (0)
      • 자료구조 (1)
      • 알고리즘 (0)
      • ✨회고 (5)
      • 포꾸 (0)
      • 인터뷰 (0)
      • 개발일지 (0)
      • 일기 (1)
      • etc (3)
      • 정처기 실기 (0)
        • C (0)
        • Java (0)
        • Python (0)
      • fonts (0)
      • articles (0)
      • 도서 (0)
  • 인기 글

  • 태그

    useState
    안티그래비티
    WEB
    SSG
    HTML
    리액트
    Next.js
    클로저
    React Query
    Til
    자바스크립트
    state
    유효성 검사
    React
    React.JS
    useEffect
    css
    TypeScript
    CSR
    SSR
    상태 관리
    프론트엔드
    아키텍처
    javascript
    폼
    리팩토링
    슬라이딩 윈도우
    zustand
    조합 패턴
    GIT
  • 최근 글

  • 전체
    오늘
    어제
  • hELLO· Designed By정상우.v4.10.3
쥬피썬더의노예
Promise.all() 사용하기
상단으로

티스토리툴바