JavaScript

[새로운 기능] Promise_03 Promise 기본 사용법

  • -

이번 포스트에서는 Promise의 chaining에 대해 살펴보자.

Promise의 Chaining

하지만 간단한 Promise의 사용은 기존 방식 대비 코드가 훨씬 복잡해진 느낌이다. 분명 좋다고 했는데.. 

then과 catch의 반환

Promise의 then이나 catch는  다시  Promise 인스턴스를 반환하는데 이 Promise의 동작은 then 및 catch의 handler의 반환값에 따라 약간씩 다르다.

then, catch의 리턴과 handler의 return이 다르다는 점을 유념하자!! 

 

  1. handler가 특정 값(ⓐ)이나 resolve된 promise(ⓑ)을 반환하는 경우
    • then/catch에서 반환된 promise는 ⓐ 또는 ⓑ의 결과값을 다음 then의 handler에게 전달한다.
  2. handler에서 오류가 발생(ⓐ)하거나 reject된 promise(ⓑ)을 반환 경우
    • then/catch에서 반환된 promise는 ⓐ 또는 ⓑ의 결과값을 다음 catch의 handler에게 전달한다.
  3. handler가 대기중인(새로운)  promise를 반환할 경우 
    • promise의 이행 여부와 결과값에 따라 다음 then/catch를 호출한다.

 

이처럼 then이나 catch가 다시 Promise를 반환하기 때문에 promise.then(a).then(b).then(c)와 같이 Promise Chain을 구성할 수 있다.

다음의 코드를 살펴보고 실행 결과를 예측해보자. (Promise.resolve(): 성공하는 Promise, Promise.reject(): 실패하는 Promise)

const p1 = Promise.resolve("1");
const p2 = p1.then((val) => {
    console.log(`p2, value = ${val}`);
    return "2";                        // 특정 값 반환
});
const p3 = p2.then((val) => {
    console.log(`p3, value = ${val}`);
    throw "3";                         // exception 반환
});
const p4 = p3.catch((reason) => {
    console.log(`p4, reason = ${reason}`);
    return Promise.resolve("4");       // resolve된 promise 반환
});
const p5 = p4.then((val) => {
    console.log(`p5, value = ${val}`);
});
console.log("done");

 

chaining

then 또는 catch가 계속해서 Promise 객체를 반환하기 때문에 계속해서 chaining을 이용할 수도 있다.(이것이 일반적인 활용법이다.) 

다음은 then에서 Pending 상태의 Promise가 반환된 이후의 동작을 chaining으로 구현한 예이다. 다소 헤깔리겠지만 출력될 값들의 순서를 추측해보자.

Promise.resolve("foo")
    .then(function (string) {
      // Pending 상태의 Promise 반환 - 여기에서의 동작(resolve/reject)에 의해 다음 동작 결정
      return new Promise(function (resolve, reject) {
          console.log(`1. ${string}`);
          setTimeout(function () {
              string += "bar";
              resolve(string);
          }, 1000);
      });
    })
    .then(function (string) {
        console.log(`2. ${string}`);
        setTimeout(function () {
            string += "baz";
            console.log(`3. ${string}`);
        }, 1000);
        return string;
    })
    .then(function (string) {
        console.log(`4. ${string}`);
    });
    
console.log(`5. done`);

 

여러분이 예상한대로 출력의 순서는 5, 1, 2, 4, 3 이다.

 

탈출 callback hell

 

이처럼 resolve나 reject는 대기중인 다른 promise(새로 생성해서 실행하지 않은 Promise)를 반환할 수도 있다. 즉 원하는 비 동기 작업을 순서대로 연결할 수 있게 된 것이다.!!

const promise = new Promise((resolve, reject) => {
    setTimeout(() => {
      console.log(`promise: 1 번째`);
      resolve();
    }, 1000);
  })
    .then(() => {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          console.log(`promise: 2 번째`);
          resolve();
        }, 1000);
      });
    })
    .then(() => {
      return new Promise((resolve, reject) => {
        setTimeout(() => {
          console.log(`promise: 3 번째`);
          resolve();
        }, 1000);
      });
    });

이로써 callback hell을 탈출 했지만 then 지옥에 온것 같은 느낌은 나만 느끼는 것일까?

 

유용한 정적 함수들

then, catch, finally 이외에도 Promise에는 유용한 정적 함수들이 제공된다.

resolve()와 reject()

resolve와 reject는 각각 성공/실패 상태의 인스턴스를 반환한다. resolve의 파라미터는 then에 사용되는 call back의 파라미터가 되고 reject의 파라미터는 catch에 사용되는 call back의 파라미터가 된다.

// 반드시 성공하는 녀석 - catch 함수는 필요치 않다.
const resolvedPromise = Promise.resolve("success");
resolvedPromise.then((val) => {
    console.log(`필승 ${val}`);
});

// 반드시 실패하는 녀석 - then 함수는 필요치 않다.
const rejected = Promise.reject("fail");
rejected.catch((val) => {
    console.log(`필패 ${val}`);
});

 

all()

all은 파라미터로 Promise 객체를 저장한 배열 등 Iterable 타입을 받는데 이 모든 작업들이  성공하면 then으로 연결된다. 이때 각각의 Promise가 resolve에게 넘겨주는 값들은 Promise.all과 연결되는 then의 callback에 Iterable 타입으로 전달된다.

const waitMe = function (name) {
    return new Promise((resolve, reject) => {
        // something todo
        const time = Math.random() * 2000;
        setTimeout(() => {
            console.log(`이름: ${name}, 대기시간: ${time}`);
            resolve(name);
        }, time);
    });
};

// then 콜백 파라미터에는 각각의 promise들이 반환한 값들이 모여서 전달
Promise.all([waitMe("hong"), waitMe("jang"), waitMe("kim")]).then((names) => {
    console.log(`모두 종료 : ${names}`);
});

 

[다음 관련 글]

 

[새로운 기능] fetch API

이번 포스트에서는 fetch api에 대해서 살펴보자.fetch는 '가져오다'라는 뜻으로 Request나 Response와 같은 객체를 이용해서 HTTP 파이프라인을 구성하는 요소를 조작하고 원격지에서 정보를 가져오기

goodteacher.tistory.com

 

 

Contents

포스팅 주소를 복사했습니다

이 글이 도움이 되었다면 공감 부탁드립니다.