본문 바로가기
개인공부/모던 자바스크립트 Deep Dive

45장 Promise(1)

by 강물둘기 2023. 1. 24.

* 아래 내용은 이웅모 저자님의  모던 자바스크립트 Deep Dive 책(위키북스)을 정리한 내용입니다.

   저작권에 문제가 된다면 삭제하도록 하겠습니다.

 

 

비동기 처리를 위한 콜백 패턴의 단점

비동기 함수는 기존 코드가 끝난 후에 실행되기 때문에 비동기로 받아온 데이터의 경우 그냥 처리하면 기대한 결과를 얻지 못한다.

// GET 요청을 위한 비동기 함수
const get = url => {
  const xhr = new XMLHttpRequest();
  xhr.open('GET', url);
  xhr.send();

  xhr.onload = () => {
    if (xhr.status === 200) {
      // ① 서버의 응답을 반환한다.
      return JSON.parse(xhr.response);
    }
    console.error(`${xhr.status} ${xhr.statusText}`);
  };
};

// ② id가 1인 post를 취득
const response = get('https://jsonplaceholder.typicode.com/posts/1');
console.log(response); // undefined

 

따라서 비동기로 받은 데이터처리는 비동기 함수 내부에서 수행해야한다. 비동기 처리가 성공하면 호출될 콜백 함수를 전달하면 제대로된 처리를 할 수 있다.

const get = (url, successCallback, failureCallback) => {
  const xhr = new XMLHttpRequest();
  xhr.open('GET', url);
  xhr.send();

  xhr.onload = () => {
    if (xhr.status === 200) {
      // 서버의 응답을 콜백 함수에 인수로 전달하면서 호출하여 응답에 대한 후속 처리를 한다.
      successCallback(JSON.parse(xhr.response));
    } else {
      failureCallback(xhr.status);
    }
  };
};

// 서버의 응답에 대한 후속 처리를 위한 콜백 함수를 비동기 함수인 get에 전달해야 한다.
get('https://jsonplaceholder.typicode.com/posts/1', console.log, console.error);
/*
{
  "userId": 1,
  "id": 1,
  "title": "sunt aut facere ...",
  "body": "quia et suscipit ..."
}
*/

 

비동기가 여러번 중첩되면 콜백함수를 중복으로 사용하게 되는 콜백 헬(callback hell)에 빠질 수 있다.

get('/step1', a => {
  get(`/step2/${a}`, b => {
    get(`/step3/${b}`, c => {
      get(`/step4/${c}`, d => {
        console.log(d);
      });
    });
  });
});

 

콜백 함수 패턴은 에러처리에도 문제가 있다.

try {
  setTimeout(() => { throw new Error('Error!'); }, 1000);
} catch (e) {
  // 에러를 캐치하지 못한다
  console.error('캐치한 에러', e);
}

setTimeout 함수는 비동기 함수이기 때문에 인자로 전달된 내부 콜백 함수는 setTimeout 함수가 호출한 것이 아니다. 따라서 에러가 발생해도 catch블록에서 캐치하지 못한다.

 

이러한 비동기 패턴의 단점을 극복하기 위해 ES6에서 프로미스(Promise)가 도입되었다.

 

Promise 생성

const promise = new Promise((resolve, reject) => {
  // Promise 함수의 콜백 함수 내부에서 비동기 처리를 수행한다.
  if (/* 비동기 처리 성공 */) {
    resolve('result');
  } else { /* 비동기 처리 실패 */
    reject('failure reason');
  }
});

 

프로미스는 현재 비동기 처리가 어떻게 진행되고 있는지를 나타내는 상태 정보를 갖는다.

상태 정보 의미 상태 변경 조건
pending 비동기 처리가 아직 수행되지 않은 상태 프로미스가 생성된 직후 기본 상태
fulfilled 비동기 처리가 수행된 상태(성공) resolve 함수 호출
rejected 비동기 처리가 수행된 상태(실패) reject 함수 호출

 

https://ko.javascript.info/promise-basics

 

프로미스의 후속 처리 메서드

프로미스의 비동기 처리 상태가 변화하면 후속 처리 메서드에 인수로 전달한 콜백 함수가 선택적으로 호출된다. 이때 후속 처리 메서드의 콜백 함수에 프로미스의 처리 결과가 인수로 전달된다. 모든 후속 처리 메서드는 프로미스를 반환하며 비동기로 동작한다.

 

Promise.prototype.then

then 메서드는 두개의 콜백 함수를 인수로 전달받는다.

첫번째 콜백함수는 resolve 함수가 호출된 상태가 되면 호출된다. 프로미스의 결과값을 인수로 전달받는다.

두번째 콜백함수는 reject 함수가 호출된 상태가 되면 호출한다. 프로미스의 에러를 인수로 전달받는다.

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

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

then 메서드는 언제나 프로미스를 반환한다.

 

Promise.prototype.catch

catch 메서드는 한개의 콜백 함수를 인수로 전달받는다. catch 메서드의 콜백 함수는 프로미스가 rejected 상태인 경우에만 호출된다.

new Promise((_, reject) => reject(new Error('rejected')))
  .catch(e => console.log(e)); // Error: rejected

catch 메서드 역시 언제나 프로미스를 반환한다.

then 메서드의 두번째 인수로 콜백 함수를 주는것과 동일하게 동작한다.

Promise.prototype.finally

finally 메서드는 한 개의 콜백 함수를 인수로 전달받는다. 성공, 실패와 상관없이 무조건 한 번 호출된다.

new Promise(() => {})
  .finally(() => console.log('finally')); // finally

finally 메서드도 언제나 프로미스를 반환한다.

 

 

 

Reference

- 이웅모 ,  모던 자바스크립트 Deep Dive , 위키북스 , 2020 

'개인공부 > 모던 자바스크립트 Deep Dive' 카테고리의 다른 글

46장 제너레이터와 async/await  (0) 2023.01.25
45장 Promise(2)  (0) 2023.01.24
44장 REST API  (0) 2023.01.23
43장 Ajax  (0) 2023.01.21
42장 비동기 프로그래밍  (0) 2023.01.20

댓글