[programmers] TIL_DAY-17

김민기·2022년 4월 6일
0

Programmers_TIL

목록 보기
17/21
post-thumbnail

✅ Promise

콜백 헬에서 벗어나자!

 자바스크립트에서 비동기 처리를 위해 사용하는 콜백 패턴은 에러 처리가 곤란하며, 여러 개의 비동기 처리가 필요할 경우 콜백 헬을 발생시킨다.

Promise 생성

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

 Promise 생성자를 new 연산자와 함께 호출하면 Promise 객체를 생성한다. Promise 생성자 함수는 비동기 처리를 수행할 콜백 함수를 인수로 전달 받는다. 이 콜백 함수는 resolve, reject 함수를 인수로 전달 받는다.

const promiseGet = url => {
  return new Promise((resolve, reject) => {
    const xhr = new XMLHttpRequest();
    xhr.open("GET",url);
    xhr.send();
    
    xhr.onload = () => {
      if(xhr.status === 200) {
        resolve(JSON.parse(xhr.response));
      } else {
        reject(new Error(xhr.status));
      }
    };
  });
};

promiseGet("https://jsonplaceholder.typicode.com/posts/1");  

  Promise 생성자 함수가 인수로 전달 받은 콜백 함수 내부에서 비동기 처리를 수행한다. 비동기 처리가 성공하면 resolve 함수를 호출하고, 실패하면 reject 함수를 호출한다. 이 둘은 모두 Promise 객체다. 따라서 promiseGet 함수는 Promise 객체를 반환한다.

Promise의 상태

  • pending : 비동기 처리가 아직 수행되지 않은 상태 (Promise가 생성된 직후 기본 상태)

  • fulfilled : 비동기 처리가 성공해서 resolve 함수를 호출한 상태

  • rejected : 비동기 처리가 실패해서 reject 함수를 호출한 상태

  • settled : 비동기 처리가 수행된 상태로 pending이 아닌 fulfilled 또는 rejected 상태

    한번 settled 상태가 되면 다른 상태로 변화할 수 없다.

then

 Promise의 비동기 처리 상태가 변화하면 이에 따른 후속 처리를 해야 한다. 비동기 처리가 성공일 경우와 실패일 경우에 대해 후속 처리를 해야 한다. 만약 콜백 패턴을 사용한다면 계속해서 콜백함수를 호출해야 하는 콜백 헬을 만들 수 있다.

 Promise.then은 두 개의 콜백 함수를 인자로 전달 받는다. Promise가 fulfilled 상태일 때 호출되는 함수와 rejected 상태일 때 호출되는 함수를 받는다.

new Promise(resolve => resolve("fulfilled"))
	.then(v => console.log(v), e=> console.log(error));

new Promise((_, reject) => reject(new Error("rejected")))
	.then(v => console.log(v), e=> console.error(e));

then은 언제나 Promise를 반환한다. 만약 then의 콜백 함수가 Promise가 아닌 값을 반환하면 그 값을 암묵적으로 resolve 또는 reject 하여 Promise를 생성해 반환한다.

Promise 에러처리

 콜백 패턴과 달리 Promise는 에러를 문제 없이 처리할 수 있다. 비동기 처리 결과에 대한 후속 처리는 Promise가 제공하는 후속 처리 메소드 then, catch, finally를 사용하여 수행한다. 비동기 처리에서 발생한 에러는 then 메소드의 두번 째 콜백 함수 또는 catch로 처리할 수 있다.

const wrongUrl = "https://jsonplaceholder.typicode.com/xxx/1";

promiseGet(wrongUrl).then(
  res => console.log(res),
  err => console.log(err)
  );

promiseGet(wrongUrl)
  .then(res => console.log(res));
  .catch(err => console.error(err));

  then 메소드의 두 번째 콜백 함수에서 에러처리를 하면 첫 번째 콜백함수에서 에러가 발생할 경우 에러를 캐치하지 못한다. 따라서 catch 메소드를 사용해서 에러를 처리해야 then 메소드 내부에서 발생한 에러까지 모두 처리할 수 있다.

Promise Chaining

 Promise의 후속 처리 메소드 then, catch, finally는 항상 Promise를 반환하므로 연속적으로 호출할 수 있다.

const url = "https://jsonplaceholder.typicode.com";

promiseGet(`${url}/posts/1`)
  .then(({userId}) => promiseGet(`${url}/users/${userId}`))
  .then(userInfo => console.log(userInfo))
  .catch(err => console.error(err));

 promiseGet을 url과 /posts/1을 사용해서 실행한다. 처리 결과 또한 Promise 객체이기 때문에 userId를 사용해서 then을 실행하고 그 처리 결과에 또한 Promise 객체가 된다. 다시 Promise 객체를 갖고 then을 실행한다. 만약 비동기 처리 과정에서 에러가 발생한다면 catch에서 에러를 처리한다.

 Promise는 Promise chaining을 통해 비동기 처리 결과를 전달받아 후속 처리를 하므로 비동기 처리를 위한 콜백 패턴에서 콜백 헬이 발생하지는 않는다. 하지만 Promise도 콜백 패턴을 사용하므로 콜백 함수를 사용하지 않는 것은 아니다. 콜백 패턴을 사용하면 가독성이 떨어진다. ES8에 추가된 Async/Await을 사용해서 가독성 좋게 비동기 처리를 동기 처리처럼 바꿀수 있다. 하지만 그 전에 Promise의 동작을 잘 이해하고 있어야 한다.

Promise method

  • Promise.all
    여러 개의 Promise를 병렬 처리할 때 사용된다. 실행 시간에 관계 없이 배열의 가장 첫 번째 Promise가 resolve한 결과 부터 차례대로 배열에 저장한 다음 그 배열을 resolve 한다. Promise.all은 배열로 전달 받은 모든 Promise가 fulfilled 상태가 되면 종료한다.

    만약 하나라도 rejected 상태가 되면 나머지 Promise 상태를 기다리지 않고 즉시 종료한다.

  • Promise.race
    Promise.all과 달리 모든 Promise가 fulfilled 상태가 되는 것을 기다리는 것이 아니라 가장 먼저 fulfilled 상태가 된 Promise의 처리 결과를 resolve 하는 새로운 Promise 객체를 반환 한다.

microtest queue

 콜백 함수는 바로 콜 스택에 쌓이지 않고 태스크 큐에 쌓인다. 콜 스택이 비면 이벤트 루프에 의해서 태스크 큐에 있는 콜백 함수가 콜 스택으로 이동하고 실행한다.

 마이크로태스크 큐는 일반적인 태스크 큐와 달리 Promise의 후속 처리 메서드의 콜백 함수가 저장된다. 마이크로태스크 큐는 태스크 큐보다 우선순위가 높다. 따라서 이벤트 루프는 콜 스택이 비었을 때, 마이크로 태스크 큐에 있는 함수를 먼저 실행하고 태스크 큐에 있는 함수를 실행한다.

참고: 모던 자바스크립트 Deep Dive

✅ 정리

  Async/Await을 배우면서 Promise는 필요 없다 생각했던 내 자신이 부끄럽다...Promise의 동작 원리를 정확하게 이해하고 있어야 Async/Await을 잘 활용할 수 있으니 Promise에 대한 공부를 계속 해야겠다 😅

0개의 댓글