본문 바로가기
JavaScript

자바스크립트 Promise에 대하여

by 붕어사랑 티스토리 2023. 2. 7.
반응형

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises

 

Using promises - JavaScript | MDN

A Promise is an object representing the eventual completion or failure of an asynchronous operation. Since most people are consumers of already-created promises, this guide will explain consumption of returned promises before explaining how to create them.

developer.mozilla.org

 

 

1. 개요

프로미스란 비동기 작업에대한 결과를 나타내는 오브젝트이다.

 

 

 

2. 프로미스가 나온 이유, 체이닝 기법

비동기 작업을 수행할 때 우리는 보통 콜백을 2개 달아준다

 

  • 비동기 작업이 성공했을 때의 콜백
  • 비동기 작업이 실패했을때의 콜백

 

과거 프로미스가 없을 시절에 자바스크립트는 비동기 작업시 아래와같이 콜백지옥에 빠지게 된다

 

doSomething(function (result) {
  doSomethingElse(result, function (newResult) {
    doThirdThing(newResult, function (finalResult) {
      console.log(`Got the final result: ${finalResult}`);
    }, failureCallback);
  }, failureCallback);
}, failureCallback);

 

위 코드를 보면 딱봐도 이해도 안되고 분석하려 하니 시작부터 어지럽다.

 

하지만 프로미스가 나온 이후로 아래와 같이 깔끔하게 정리된다

 

doSomething()
  .then(function (result) {
    return doSomethingElse(result);
  })
  .then(function (newResult) {
    return doThirdThing(newResult);
  })
  .then(function (finalResult) {
    console.log(`Got the final result: ${finalResult}`);
  })
  .catch(failureCallback);

 

Arrow Function을 이용하면 더 깔끔하게 정리 할 수 있다

doSomething()
  .then((result) => doSomethingElse(result))
  .then((newResult) => doThirdThing(newResult))
  .then((finalResult) => {
    console.log(`Got the final result: ${finalResult}`);
  })
  .catch(failureCallback);

 

 

 

3. 사용법

먼저 프로미스를 선언하는 방법은 다음과 같다

const promise1 = new Promise((resolve, reject) => {
  ///
  작업 주르르르르륵~
  ///
  if(success) {
    resolve(결과값);
  }
  else {
    reject(new Error("Request is failed"));
  }
});


promise1.then(successCallback, failueCallback);

 

  • resolve와 reject 함수를 전달하는 ArrowFunction을 프로미스를 생성할 때 인자로 전달한다
  • 작업코드를 작성하고 작업이 성공하였을 시 resolve(결과값) 메소드를 호출하여 결과값을 전달한다
  • 작업이 실패할 경우 reject를 호출한다
  • 프로미스.then을 사용하여 작업이 성공하면 successCallback을 호출하고, 실패하면 failueCallback을 호출한다

 

 

catch

캐치는 then(null, failueCallback)의 shortcut 버전이다. 앞선 예제에서 여러개의 프로미스에 대하여 에러핸들링이 동일할경우 앞선 예제코드처럼 작성이 가능하다. 

doSomething()
  .then((result) => doSomethingElse(result))
  .then((newResult) => doThirdThing(newResult))
  .then((finalResult) => {
    console.log(`Got the final result: ${finalResult}`);
  })
  .catch(failureCallback);

원리는 다음과 같다

 

  • 체이닝 도중에 에러가 나는경우, 브라우저는 failueCallback이나 catch를 찾을 때 까지 체이닝을타고 쭈르륵 내려간다

 

 

 

 

 

 

4. 자주하는 실수

아래는 프로미스 사용시 자주하는 실수라고 한다

// Bad example! Spot 3 mistakes!

doSomething()
  .then(function (result) {
    // Forgot to return promise from inner chain + unnecessary nesting
    doSomethingElse(result).then((newResult) => doThirdThing(newResult));
  })
  .then(() => doFourthThing());
// Forgot to terminate chain with a catch!

 

총 3가지의 실수가 있다

  • 첫번째 실수는 체이닝이 broken되었다. doSomething에서 리턴값이 없다. 즉 doFourthThing()은 doSomethingElse를 기다리지 않는다. ascyn한 작업의 체이닝이 동시에 두개가 진행되고 있는 것 이다
  • 두번쨰 실수는 프로미스안에 프로미스를 nesting했다. 이는 첫번째 실수를 자주 유발한다
  • 세번째 실수는 체이닝을 catch로 끝내지 않았다는 점 이다.

 

이를 고치면 아래와같이 된다.

doSomething()
  .then(function (result) {
    // 체이닝을 할 때는 항상 프로미스를 리턴하자
    return doSomethingElse(result);
  })
  // Arrow Function을 한줄로 사용할때는 항상 중괄호를 없애서 값을 리턴하자
  .then((newResult) => doThirdThing(newResult))
  // 앞선 프로미스에서 결과값을 리턴해도, 리턴값이 필요없다면 아래처럼 값을 무시해도 된다
  .then((/* result ignored */) => doFourthThing())
  // 프로미스 체이닝은 항상 catch로 끝내서 에러값을 핸들링하자
  .catch((error) => console.error(error));

 

 

여기서 한가지 중요한 내용이 있다

 

Arrow Function에서 함수가 한줄로 끝나는형태, 예를들어 () => x 는 () => { return x;} 와 동일하다.

그러므로 한줄함수를 작성할 때 실수로 중괄호를 넣는 행위는 하지 말자

 

그렇지 않으면 return값이 없어져서 channing이 끊기게 된다

 

 

 

 

대충 여기까지 읽었으면 프로미스의 기본적인 내용은 다 배웠다고 해도 무방하다

 

 

 

 

 

5. 프로미스 reject 이벤트

프로미스 체이닝에서 reject 이벤트가 발생하고, 이를 적절한 에러핸들링을 해주지 않을 경우, 이 reject 이벤트는 콜스택을 따라 버블링하여 쭈르르륵 올라가게된다. 그리고 호스트는 이를 표면에 드러낼 필요가 있다.

 

 

웹의경우, 프로미스가 reject되면 global scope(브라우저에서는 window, web worker에서는 Worker)에서 두가지 이벤트중 하나가 핸들러 처리여부와 상관없이 반드시 발생한다.

 

unhandledrejection

reject된 프로미스가 핸들러에 의해 처리되지 않을경우 발생하는 이벤트이다.

 

rejectionhandled

reject된 프로미스가 핸들러에의해 처리되었을경우 발생하는 이벤트이다.

 

 

 

두 이벤트 모두 멤버변수로 promise와 reason이라는걸 가지게 된다. promise는 문제를 일이킨 프로미스이며, reason은 문제가 발생한 원인을 나타낸다

 

 

이 두가지 이벤트는, 에러핸들링을 적절히 해주지 않을때를 대비한 최후의 대비책이다. 그리고 결국 이놈들도 에러 핸들러이며 범위가 global context에 속하기 때문에 모든 에러들이 이를 거쳐가게 된다.

 

 

 

Node.js의 경우 아래와같이 unhandledRejection 이벤트를 추가하여 전역으로 에러핸들링을 할 수 있다. 

process.on("unhandledRejection", (reason, promise) => {
  // Add code here to examine the "promise" and "reason" values
});

 

 

 

6. 프로미스의 상태

프로미스는 세가지 상태가 있다

 

  • pending : 초기상태이며, fulfilled나 reject 되지 않은 상태이다
  • fulfilled : 프로미스의 작업이 성공적으로 완료된 상태
  • rejected : 프로미스의 작업이 실패한 상태
반응형

'JavaScript' 카테고리의 다른 글

자바스크립트 this에 대하여  (0) 2023.02.02
자바스크립트 상속 그리고 프로토타입이란  (0) 2023.01.31

댓글