Promise의 기본 공부

맨날·2021년 6월 28일
0

기본 개념

Promise는 비동기를 처리할때 사용이 되는 객체입니다. Promise가 나오기 전에는 콜백 함수를 통해서 비동기 처리가 완료된 후의 작업을 처리하였습니다.

setTimeout은 비동기 처리시 사용되는 대표적인 함수입니다. setTimeout 은 첫번째 파라미터로 타이머가 만료된 뒤 실행 될 콜백함수를 받습니다.

function sleep(callback) {
  setTimeout(callback, 1000);
} 

sleep(() => {
  console.log('좋은 아침!');
});

콜백 지옥

콜백 지옥은 비동기 처리를 위하여 콜백함수를 연속으로 사용될 때 문제가 발생합니다. 아래와 같은 코드가 콜백 지옥의 하나의 예입니다. 아래의 코드는 별로 복잡해 보이지 않을 수도 있지만, 실제 로직이 들어가게 되면 경우에는 코드의 흐름도 파악하기 힘들고 가독성이 매우 떨어지는 코드가 됩니다.

setTimeout(() => {
  fn1(() => {
    fn2(() => {
      fn3(() => {
        fn4(() => {
          // ...
        })
      })
    })
  })
});

Promise

Promise의 등장으로 이전의 콜백 지옥에서 벗어날 수 있게 되었습니다. 아래 코드처럼 데이터 흐름이 위에서 아래로 향하고 있어 콜백 지옥보다는 코드를 파악하기 쉬워집니다.

const promise = new Promise((resolve) => {
  setTimeout(() => {
    resolve(true);
  });
})

promise
  .then(() => {

  })
  .then(() => {

  })
  .then(() => {

  })
  .then(() => {

  })

Promise의 상태 3가지

프로미스 객체가 생성 되고 완료될때까지 프로미스는 아래의 상태 중 한가지를 가집니다.

  • Pending
  • Fulfilled
  • Rejected

Pending

Pending 상태는 아직 비동기 처리가 완료되지 않은 상태로 이행 또는 거부 될때까지 대기합니다.

아래와 같이 프로미스 객체를 생성하는 경우 Pending 상태가 됩니다.

new Promise();

Fulfilled

Fulfilled 상태는 비동기 처리가 성공으로 완료된 상태를 나타냅니다.

아래와 같이 Promise 객체를 생성할 때 콜백함수를 인자로 넘겨 줄 수 있습니다. 콜백함수가 호출 될 때 resolve 함수를 넘겨주는데 이 resolve 함수를 호출하면 프로미스는 Fulfilled 상태가 되며 값과 함께 반환하게 됩니다.

new Promise((resolve) => {
  resolve(); // 값은 생략가능
});

Fulfilled 상태가 되면 해당 프로미스에 연결된 then 메소드에 값과 함께 비동기 처리 이후의 로직을 처리할 수 있습니다.

then 메소드를 호출하면 새로운 Promise 객체를 반환해 줍니다. than 콜백함수 내부에서 return 하는 값은 fulfilled 의 값이 됩니다(다음 then 처리시 콜백함수의 파라미터로 전달되는 값).

const promise = new Promise((resolve) => {
  const value = 1;
  resolve(value);
});

promise.then(response => {
  console.log(response);
});

Rejected

Rejected 상태는 비동기 처리가 거부된 상태를 나타냅니다.

콜백함수의 두번째 인자로 reject 함수를 넘겨주며, 해당 함수를 호출하면 프로미스는 rejected 상태가 됩니다.

new Promise((resolve, reject) => {
  reject(); // 값은 생략가능
});

Rejected 상태가 되면 프로미스에 연결된 catch 메소드를 호출합니다. reject함수를 호출할때 넘겨주었던 파라미터를 콜백함수의 인자로 받을 수 있습니다.

const promise = new Promise((resolve, reject) => {
  reject(new Error('에러 발생!'));
});

promise.catch(error => {
  console.log(error);
});

then, catch

위에서 언급했었던 후속 처리 방법으로 아래와 같이 체이닝을 통해 사용할 수 있습니다.

const promise = new Promise((resolve, reject) => {
  // 처리
});

promise
  .than(() => {
    // 이행에 대한 후속 처리
  })
  .catch(() => {
    // 거부에 대한 후속 처리
  });

then 메소드에서 두번째 파라미터로 콜백함수를 넘겨주면 reject에 대한 처리도 가능합니다.

const promise = new Promise((resolve, reject) => {
  // 처리
});

promise
  .then(() => {
    // 이행에 대한 후속 처리
  }, () => {
    // 거부에 대한 후속 처리
  })

헷갈렸던 점

then 이후의 후속처리

  1. then은 어떻게 계속 체이닝 되는 것인가.

    후속 처리 메소드인 then와 catch의 경우 실행 후 프로미스를 반환한다. 두 메소드 내부에서 return 되는 값을 프로미스의 값으로 가진다.

const promise = new Promise((resolve) => {
  resolve();
});

const promise2 = promise.then(() => {
  return 10;
});

console.log(promise2) // Promise {<fulfilled>: 10}

위의 결과와 같이 프로미스를 반환하기 때문에 후속 처리 메소드를 연속적으로 사용이 가능하다. 이때 프로미스의 값은 콜백 함수의 파라미터로 넘어온다.

const promise = new Promise((resolve) => {
  resolve();
});

promise
  .then(() => {
    return 10;
  })
  .then(res => {
    console.log(res); // 10
  });
  1. then 내부에서 새로운 프로미스를 실행할 때 pending 상태가 되게 하려면 어떻게 해야 하는가.

    then 메소드 내부에서 promise를 반환하면 pending 상태가 된다.

function sleep() {
  return new Promise((resolve) => {
    setTimeout(function() {
      console.log('javascript');
      resolve()
    }, 10000);
  });
}

const promise = new Promise((resolve) => {
  resolve();
});

promise
  .then(() => {
    console.log('hello');
    sleep();
  })
  .then(() => {
    console.log('bye')
  });

위의 콘솔에 출력되는 결과는 hello bye javascript 이다.

하지만 우리가 원하는 것은 hello javascript bye 순으로 출력 되는 것이다. 현재 문제는 프로미스가 pending 상태가 되지 않고 바로 then 메소드가 실행되고 있다. 그렇다면 pending 상태가 되도록 하려면 어떻게 해야 하는가.

function sleep() {
  return new Promise((resolve) => {
    setTimeout(function() {
      console.log('javascript');
      resolve()
    }, 10000);
  });
}

const promise = new Promise((resolve) => {
  resolve();
});

promise
  .then(() => {
    console.log('hello');
    return sleep();
  })
  .then(() => {
    console.log('bye')
  });

위와 같이 return 으로 프로미스를 넘겨주면 된다. 그러면 우리가 생각했던대로 비동기를 처리하더라도 동기적으로 순서대로 호출이 되는 것을 확인할 수 있다.

0개의 댓글