Front-end/JavaScript

JS 에러 핸들링 3편 - promise catch

파리외 개발자 2023. 7. 31. 22:40

일반적인 try catch문의 문제는 비동기적 코드에는 대응하지 못한다는 것이다.

그렇기 때문에 Promise에는 비동기만을 위해 지원하는 catch문이 따로 존재한다.

 

Promise catch문 사용법

Promise.resolve("async fail")
  .then((res) => {
    throw new Error("#1 fail");
    return res;
  })
  .then((res) => {
    console.log(res);
  })
  .catch((err) => {
    console.log(err);
    return err;
  })

Promise에는 정상동작일 때를 위한 then문과

오류처리를 위한 catch문이 존재한다.

첫 번째 then문에서 의도적으로 #1 fail이라는 에러를 생성했고

따라서 두 번째 then문을 패스하여 마지막의 catch문이 실행되게 된다.

 

catch문을 최하단에 적어줘야 하는 이유

Promise.resolve("async fail")
  .then((res) => {
    throw new Error("#1 fail");
    return res;
  })
  .then((res) => {
    console.log(res);
  })
  .catch((err) => {
    console.log(err);
    return err;
  })
  .then((res) => {
    console.log(res.message);
    throw new Error("#2 fail");
  })
  .catch((err) => {
    console.log("final err", err);
  });

위 코드에서 then문과 catch문을 하나씩 더 추가해 봤다.

첫 번째 catch문에서 #1 fail에러를 잡아냈고

마지막 catch문은 #2 fail에러를 잡아냈다.

이는 첫 번째 catch문 다음의 then문이 정상 동작하여

에러를 다시 뱉어내줬다는 것을 알 수 있다.

 

catch문을 제일 하단에 적어주지 않는다면 Promise는 멈추지 않고 계속 다음 동작을 이어할 수 있다는 것이다.

 

각 Promise 블록에 해당하는 catch문을 작성할 것

Promise.resolve("async fail")
  .then((res) => {
    Promise.resolve().then(() => {
      throw new Error("#3 fail");
    }); //this promise block doesn't handling err
    return res;
  })
  .then((res) => {
    console.log(res);
  })
  .catch((err) => {
    console.log(err);
    return err;
  })
  .then((res) => {
    console.log(res.message);
    throw new Error("#2 fail");
  })
  .catch((err) => {
    console.log("final err", err);
  });

첫 번째 then절 안에 Promise가 중첩해서 정의되었다.

그리고 해당 Promise에는 catch문이 없다.

#3 fail에러는 이 상황에서 잡아내지 못한다.

실행을 해보면 위 코드는 완전히 엉망진창이 돼버린다.

첫 번째 then절에서 기대했던 #3 에러를 잡아내기는커녕 res를 그대로 리턴해버리고

두 번째 then절은 해당 res를 그대로 콘솔에 찍어낸다 -> async fail

세 번째 catch문은 동작하지 않고

네 번째 then절에서 res.message를 출력하려다 res가 undefined라서 message속성을 찾지 못한다.

당연히 해당 console.log에서 에러가 나면서 #2 에러도 동작하지 않고

마지막 catch문은 비동기 에러를 잡는 것이 아닌 res의 message에 접근하지 못하여서 생긴

type에러를 잡아내게 된다.

 

그러니 반드시 Promise의 블록에 대응되는 곳에 catch문을 작성해야 한다.

// fix it
Promise.resolve("async fail")
  .then((res) => {
    Promise.resolve()
      .then(() => {
        throw new Error("#3 fail");
      })
      .catch(console.log);
    return res;
  })
  .then((res) => {
    console.log(res);
  })
  .catch((err) => {
    console.log("final err", err);
  });

해당하는 블록의 제일 하단에 catch문을 작성하자 원하는 데로 동작하게 된다.

우선 첫 번째 then문에 에러발생은 해당 catch문이 잡게 되고

첫 번째 then문은 종료된다.

다음 두 번째 then문에서는 res값인 async fail을 정상적으로 출력하게 되므로

final catch문은 실행될 일 없이 종료된다.

여기서 확인해야 될 것은 첫 번째 then문의 내부 Promise catch문 보다

두 번째 then문이 먼저 실행되고 에러 catch가 동작했다는 것이다.