[JS] 비동기 - Promise

가르송·2023년 3월 22일
0

Promise란?

비동기로 작동하는 코드를 제어하기 위해 사용하는 것으로, 비동기적으로 수행한 작업이 끝났음을 알려주는 프로미스 객체를 뜻한다. Callback Hell을 방지할 수 있다는 장점이 있다.

state

  • pending : 이제 막 프로미스가 만들어져서 작업이 끝나지 않은 상태
  • fulfilled : 비동기 처리를 수행하는 작업(executor(콜백 함수))가 성공적으로 작동한 상태
  • rejected : 작업 중 에러가 발생한 상태

Promise 인스턴스의 메서드

  • .then : executor에 작성했던 코드들이 정상적으로 처리된 경우 (그 실행 결과를 다루기 위해) 실행되는 메서드
  • .catch : executor에 작성했던 코드들에 에러가 발생한 경우 (에러에 접근하기 위해) 실행되는 메서드
  • .finally : executor에 작성했던 코드의 처리 성공 여부와 관련없이 마지막으로 실행되는 함수

에러 핸들링

여러 가지 프로미스가 체이닝 된 경우에는 맨 아래에서 .catch를 사용하면 어디에서 에러가 발생했는지 알 수 없다. 따라서 아래와 같이 getSeed 작업 시 발생하는 에러를 잡는 것이 중요한 상황이라면 .catch 위치를 조정하면 된다.

이렇게 할 경우 프로미스 체이닝에 지장이 생기지 않도록 리턴 값을 지정해줄 수 있어서 좋다.

getSeed()
  .catch((error) => {
    console.log(error.name);
    return "basic seed"; // 에러 발생 시 대체할 값을 지정
  })
  .then(seed => getPlant(seed))
  .then(plant => console.log(plant));

TIP. 전달하는 인자와 호출하는 인자가 동일한 경우 아래와 같이 변형할 수 있다.

// 1과 2는 같은 역할을 한다
.then(seed => getPlant(seed)) // 1
.then(getPlant) // 2 
.then(plant => console.log(plant)) // 1
.then(console.log) // 2

Promise static method

Promise.all

모든 프로미스를 병렬적으로 한 번에 실행해주는 메서드이다. 실행하고자 하는 프로미스들을 배열 안에 담아 인자로 전달하면 된다.

실습 : 메서드를 이용한 함수 만들기

이를 이용해 복수의 유저 정보 객체를 하나의 배열에 담아 resolve하는 Promise 객체를 반환하는 함수 readAllUsers를 작성해 보았다.

1. 첫 번째 시도 : 배열 안에서 .then 사용
메서드의 인자로 들어가는 배열 안에서 로직을 해결하는 것이 깔끔하다는 생각이 들어 아래와 같이 작성했다.

const readAllUsers = () => {
  return Promise.all(
    [
      getDataFromFilePromise(user1Path).then((data) => JSON.parse(data)),
      getDataFromFilePromise(user2Path).then((data) => JSON.parse(data))
    ]
  );
}

2. 두 번째 시도 : 구조 변경, .map 활용
실시간 세션에서 레퍼런스 코드를 보고 나니 배열 외부, 즉 Promise.all.then 메서드를 연결하여 추가적인 작업(JSON 변환 등)을 하는 것이 훨씬 깔끔하다는 생각이 들었다. 유저 정보가 많은 경우를 대비하여 하드코딩하지 않고 .map 메서드를 사용한 점도 인상적이었다.

세션 다음 날, 레퍼런스 코드의 장점을 살리기 위해 노력하며 스스로 다시 작성해 보았다.

const readAllUsers = () => {
  const paths = [user1Path, user2Path];
  const dataPromises = paths.map((path) => getDataFromFilePromise(path));

  return Promise.all(dataPromises)
    .then((dataList) => dataList.map((data) => JSON.parse(data)));
}

Promise.allSettled

Promise.all 과 달리 작업 중 실패한 프로미스가 있는지 여부와 관계 없이 모든 결과를 배열에 담아 반환한다.

// 실행 결과
[
  { status: 'fulfilled', value: 'Lemon cake' },
  { status: 'fulfilled', value: 'Strawberry cake' },
  { status: 'rejected', reason: Error: ... },
]

Promise.resolve

작업이 정상적으로 처리되었을 때 실행할 코드를 보다 간략하게 작성할 수 있다.

// Promise
function getPlant (seed) {
  return new Promise ((resolve, reject) => {
    resolve("plant");
  }
}

// Promise.resolve
function getPlant (seed) {
  return Promise.resolve(seed => "plant");
}
profile
개발도 운동도 뜻대로 되지 않을 때에는? 산책을 합니다.

0개의 댓글