Promise

이지훈·2021년 2월 9일
0

공부한것들

목록 보기
5/15

시작

비동기 콜백에 이어 Promise를 알아보자.

Promise란

MDN
Promise 객체는 비동기 작업이 맞이할 미래의 완료 또는 실패와 그 결과 값을 나타냅니다.
Promise는 프로미스가 생성될 때 꼭 알 수 있지는 않은 값을 위한 대리자로, 비동기 연산이 종료된 이후의 결과값이나 실패 이유를 처리하기 위한 처리기를 연결할 수 있도록 합니다. 프로미스를 사용하면 비동기 메서드에서 마치 동기 메서드처럼 값을 반환할 수 있습니다. 다만 최종 결과를 반환하지는 않고, 대신 프로미스를 반환해서 미래의 어떤 시점에 결과를 제공합니다.

비동기 작업은 지금 당장 그 결과를 알 수 없기 때문에 미래에 그 성공/실패여부와 결과값이 정해진다. Promise를 사용하면 이러한 비동기 메서드에서 마치 동기 메서드처럼 값을 반환 할 수 있다.
앞서 콜백을 사용해 비동기를 처리하는 방법을 알아봤는데, 콜백의 경우 여러번 중첩하여 사용하면 코드가 계속 길어지고 이해하기 어려워지는 이른바 콜백 지옥이 되어버린다. 하지만 적절한 상황에 Promise를 사용하면 보다 알기쉽게 사용 할 수 있다.

Promise 사용

Promise는 비동기로 실행될 함수(executor)를 Promise로 감싼 후. then(), catch(), finally()를 사용하게 된다.

new Promise에 전달된 함수(executor)가 가진 인수 resolve와 reject는 자바스크립트가 자체적으로 제공하는 콜백이다

  • resolve(value) : 일이 성공적으로 끝난 경우, 결과를 value와 함께 호출
  • reject(error) : 에러 발생 시 에러 객체 error와 함께 호출

state와 result

new Promise로 생성한 객체는 내부 프로퍼티로 state와 result를 가진다. (javascript.info 참고) 다음 그림이 잘 설명해주고 있다.

state에는 pending, fulfilled, rejected가 있다.
맨 처음에는 pending(대기) 이었다가 resolve가 호출되면 fulfilled(이행), reject가 호출되면 rejected(거부) 상태가 된다.
이때 result는 처음 pending 상태에서는 undefined였다가 resolve(value) 또는 reject(error)가 호출되면 value/error로 변한다.

한번 resolve,reject가 호출되면 처리된settled 프로미스라고 하며 그 후에는 resolve,reject를 호출해도 무시된다.

then, catch, finally

만들어진 프로미스 객체를 소비함수인 .then .catch .finally 메서드를 통해 사용하게된다

.then은 resolve를 , .catch는 reject 결과를 이어받아 처리한다.

promise.then(...).catch(...)

then에는 두개의 함수가 인수로 주어지는데 첫번째는 resolve, 두번째는 reject를 이어받아 처리한다. 하지만 reject의 에러 처리에는 catch를 많이 사용한다고 한다.

.then이후 또다시 .then, .catch를 사용하여 체인을 형성할 수 있다.

이때 then의 반환값은 프로미스이다. 만약 프로미스가 아닌 다른 객체를 반환할 시에는 이를 resolve된 promise로 감싸서 반환한다고 한다!

.catch이후에도 에러를 처리한 이후 다시 .then을 사용해 이어나갈 수 있다.

그리고 마지막으로 성공/실패여부와 상관없이 실행하고싶은 것이 있다면 finally()를 사용하면 된다.

예시?

const promise = new Promise((resolve, reject)=>{
    resolve('foo')
});
promise
  // 1. "foo"를 받고 "bar"를 추가한 다음 그 값으로 이행하여 다음 then에 넘겨줌
  .then((string) => {
      console.log('첫번째 then 진입')
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        string += 'bar';
        resolve(string);
      }, 1000);
    });
  })
  // 2. "foobar"를 받고 그대로 다음 then에 넘겨준 뒤,
  // 나중에 콜백 함수에서 가공하고 콘솔에 출력
  .then((string) => {
      console.log('두번째 then 진입')
        setTimeout(() => {
        string += 'baz';
        console.log(string);
        }, 1000)
     return string;
  })
  // 3. 이 부분의 코드는 이전의 then 블록 안의 (가짜) 비동기 코드에서
  // 실제로 문자열을 가공하기 전에 실행됨
  .then((string) => {
    console.log('세번째 then 진입');
    // 'baz' 부분은 setTimeout 함수로 비동기적으로 실행되기 때문에
    // 이곳의 string에는 아직 'baz' 부분이 없음
    console.log(string);
  });
// 로그 출력 결과 (순서대로):
// 첫번째 then 진입
// 두번째 then 진입
// 세번째 then 진입
// foobar
// foobarbaz

이 예시를 이해하는것이 어려웠지만, 이해하고나니 약간은 감을 잡을 수 있었다.

먼저 promise에서 resolve를 호출하고, 그 결과인 'foo'를 첫번째 then에서 받아 이행한다. string 안에는 'foo'가 담겨있다. 첫번째 then에선 setTimeout을 통해 1초 후에 string에 'bar'를 더하는 프로미스를 반환한다.

1초 후 그 프로미스의 결과를 두번째 then에서 받게된다. 그런데 두번째 then은 setTimeout을 통해 1초 뒤 string에 baz를 더하는 가공을 하여 출력한다. 그런데 이 동작은 프로미스가 아니다. 그러니까 이 동작은 비동기로 처리되고 처리되기 전인 'foobar'가 세번째 then으로 넘어간다.

세번째 then에서는 string을 출력하는데 이때 foobar가 찍히게된다.
그후 비동기작업이 끝나면 foobar에 baz가 더해져 출력된다.

출처
:
MDN - Promise
MDN-Promise.then()
MDN-Promise 사용법
kimcoder's dev note
then의 반환값에 대해 고민하던 부분에 많은 도움이 되었습니다.
->MDN 문서의 샘플코드를 보면 위와 같이 되어있다.
코드에서 확인할 수 있는 것처럼 첫번째 then에서 반환되는 값은 자동으로 resolved promise로 wrapping되기 때문에 다음 then함수에서 체이닝 결과를 확인할 수 있다.
(정확하게 말하자면 반환 값에 Promise객체가 아닌 다른 객체가 있으면 자동으로 resolved promise객체로 wrapping.)

profile
안녕하세요! 대학교 졸업한 이지훈입니다.

0개의 댓글