본문 바로가기
개발지식

[Javascript] Promise가 어떻게 동작하는가?

by 구라미 2024. 3. 7.

 

 

1. Promise 란?

Promise는 Javascript에서 제공하는 비동기 작업을 처리하기 위한 내장객체이다. 비동기 처리는 보통 네트워크 요청을 통해 데이터를 받아오거나, 파일 로딩, 데이터베이스 조회 등과 같이 시간이 걸리는 작업을 수행할 때 사용된다. Promise는 이러한 작업이 완료되었거나 또는 실패할 경우의 결과를 반환하고 관련 작업을 처리할 수 있도록 도와준다.

Promise 객체는 아래와 같이 세 가지 상태를 가질 수 있다.
1. 대기(pending): 작업이 아직 완료되지 않은 상태
2. 이행(fulfilled): 작업이 성공적으로 완료된 상태
3. 거부(rejected): 작업이 실패한 상태

Promise 메서드로 then(), catch(), finally()가 있는데, 특징은 각각 아래와 같다.

  • then()은 Promise가 fulfilled 또는 rejected 상태일 때 호출되며 두 개의 콜백함수를 인자로 받는데 첫번째는 fulfilled되었을 때 실행되는 콜백함수이고, 두번째는 rejected 되었을 때 실행된다. 
  • catch()는 Promise가 rejected 되었을 때 호출되는 메서드이다. 
  • finally()는 Promise가 fulfilled 되건, refected되건 실행된다.

아래는 파일을 비동기적으로 읽어올 때 Promise의 예제이다.

const fs = require('fs');

// 파일을 읽어오는 Promise를 생성합니다.
const readFilePromise = (filePath) => {
  return new Promise((resolve, reject) => {
    fs.readFile(filePath, 'utf8', (err, data) => {
      if (err) {
        reject(err); // 파일 읽기 실패 시 거부 상태로 변경
      } else {
        resolve(data); // 파일 읽기 성공 시 이행 상태로 변경
      }
    });
  });
};

// 파일을 읽어오는 Promise를 호출하고 처리합니다.
readFilePromise('example.txt')
  .then(data => {
    console.log('파일 내용:', data);
  })
  .catch(err => {
    console.error('파일 읽기 실패:', err);
  });

위 예시코드에서 readFilePromise함수를 사용하여 파일을 비동기적으로 읽어온다. 이 함수는 Promise를 반환하고, 파일을 성공적으로 읽어오면 then() 메서드를 사용하여 파일 내용을 출력하고, 실패하면 catch() 메서드를 사용하여 에러를 처리한다.

 

2. 여러개의 promise를 처리하는 방법

여러 개의 Promise를 일괄로 처리하고 싶다면 어떻게 해야할까? 여러 개의 Promise를 처리하는 메서드가 여러가지 있는데 all(), allSettled(), race()가 있다. 이들은 모두 여러 개의 Promise를 동시에 처리하고 결과를 반환하는 데 사용되는데 각각 어떤 차이가 있을까? 

1) Promise.all()

모든 Promise가 이행될 때까지 기다린 후, 모든 Promise의 결과를 배열로 반환한다. 하나라도 Rejected되면 바로 Rejected 된다.

const promise1 = Promise.resolve(3);
const promise2 = 42;
const promise3 = new Promise((resolve, reject) => {
  setTimeout(resolve, 100, 'foo');
});

Promise.all([promise1, promise2, promise3])
  .then(values => {
    console.log(values); // [3, 42, 'foo']
  })
  .catch(error => {
    console.error('에러 발생:', error);
  });



2) Promise.allSettled()

모든 Promise가 이행되거나 거부될 때까지 기다린 후, 모든 Promise가 처리된 결과를 객체의 배열로 반환한다. 결과에는 각 Promise의 상태와 값을 포함한다.

const promise1 = Promise.resolve(3);
const promise2 = new Promise((resolve, reject) => setTimeout(reject, 100, '거부됨'));
const promise3 = new Promise((resolve, reject) => setTimeout(resolve, 200, 'foo'));

Promise.allSettled([promise1, promise2, promise3])
  .then(results => {
    console.log(results);
    /*
    [
      { status: 'fulfilled', value: 3 },
      { status: 'rejected', reason: '거부됨' },
      { status: 'fulfilled', value: 'foo' }
    ]
    */
  });



3) Promise.race()

여러 개의 Promise 중 가장 먼저 이행되거나 거부된 Promise의 결과를 반환한다.

const promise1 = new Promise((resolve, reject) => setTimeout(resolve, 500, 'one'));
const promise2 = new Promise((resolve, reject) => setTimeout(resolve, 100, 'two'));

Promise.race([promise1, promise2])
  .then(result => {
    console.log(result); // 'two' (promise2가 먼저 이행됨)
  });


이러한 메서드들을 사용하여 여러 개의 Promise를 효과적으로 처리할 수 있다. 

 

3. promise에서 에러를 처리하기

Promise에서 then() 메서드, catch() 메서드 모두 Promise가 Rejected 되었을 시 콜백함수를 호출하는데  취향에 따라 다르게 작성할 수도 있지만 then()메서드의 두번째인자를 통해 호출하는 것보다 catch()메서드를 사용해서 에러 처리를 하는 편이 코드의 가독성을 높이고 의미와 역할이 분명하므로 더 권고되는 사항이다.

1) catch()

somePromiseFunction()
  .then(result => {
    // Promise가 이행될 경우 실행되는 코드
    console.log(result);
  })
  .catch(error => {
    // Promise가 거부될 경우 실행되는 코드
    console.error('에러 발생:', error);
  });



2) then() 메서드의 두 번째 인수

somePromiseFunction()
  .then(result => {
    // Promise가 이행될 경우 실행되는 코드
    console.log(result);
  }, error => {
    // Promise가 거부될 경우 실행되는 코드
    console.error('에러 발생:', error);
  });

 

댓글