비동기 처리를 시작하지 2 - 콜백지옥과 Promise

지은·2021년 6월 29일
0

ES6

목록 보기
5/6
post-thumbnail

동기 언어인 js로 비동기 처리를 하기 위한 방법은 callback 함수를 사용하는 것이다. 그렇게 callback을 매번 달아서 체인처럼 콜백 체인을 만든다면 어떨까?
간단한 경우엔 괜찮을지 몰라도 network와의 통신이나 파일을 읽는 기능이라면?
당연히 비 직관적이고 비즈니스 로직을 파악하기도 어려워져 가독성이 떨어질 것이다. 체인이 길어질 수록 에러가 발생하거나 디버깅을 하는 경우에도 굉장히 어려워지고 유지 보수도 까다로워진다. 이를 해결하기 위해 promise를 배워보자.

Promise란?
자바스크립트에서 제공하는 비동기 처리를 위한 object로 정해진 장시간의 기능을 수행한 뒤 성공(메시지와 결과값) 혹은 에러를 전달해준다.

마치 펀딩처럼 미리 결제해두고 이후에 상품을 받아보는 것처럼! promise는 두 가지에 초점을 맞춰야 한다.

  • state
    기능 수행 중인지 기능 수행을 마친 뒤 성공했는지 실패했는지 등의 상태
    pending(수행 중) -> fulfilled(수행 완료) or rejected(수행 실패)

  • producer vs consumer
    우리가 원하는 데이터를 제공하는 사람 vs 데이터를 사용하는 사람

코드로 알아보자. 우선 promise를 만드는 코드는 이러하다. 클래스이기 때문에 new를 달아주는 것을 잊으면 안 된다.

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

promise의 생성자는 executor라는 콜백 함수를 전달해 줘야 하는데, 이 콜백 함수는 두 가지의 다른 콜백 함수를 받는다.

  • resolve
    기능을 정상적으로 수행한 뒤 결과를 전달하는 콜백 함수
  • reject
    기능을 전달하다가 문제가 생겼을 때 호출하는 콜백 함수
    promise의 executor는 바로 실행되기 때문에 불필요한 네트워크 통신을 만들고 싶지 않다면 주의해야 한다.

producer

어떤 값을 수행한 뒤 오복이(우리 집 강아지)라는 값을 가져오는 producer를 만들어보자.

const promise = new Promise((resolve, reject) => {
	console.log('blah blah');
    setTimeout(() => {
    	resolve('오보깅');
        resect(new Error('no network'));
    }, 2000};
});

이걸 이용하는 코드도 추가해 보자.

consumers: then, catch, finally

promise
  .then((value) => {
      console.log(value);
  })
  .catch(error => {
  	console.log(error);
  });
  .fianlly(() => {
  	console.log('finally');
  });
  • then: 성공했을 때 수행할 행동을 관리한다.
  • catch: 실패했을 때 수행할 행동을 관리한다.
  • finally: 성공과 실패에 관계없이 무조건 수행할 행동을 관리한다.

그럼 이제 promise를 chaining 해 보자.

Promise chanining

const fetchNumber = new Promise((resolve, reject) => {
	setTimeout(() => resolve(1), 1000);
});

fetchNumber
	.then(num => num*2)
    .then(num => num*3)
    .then(num => {
    	return new Promise((resolve, reject) => {
        	setTimeout(() => resolve(num-1), 1000);
        });
    })
    .then(num => console.log(num));

then은 값을 바로 전달할 수도 있고 또 다른 promise를 전달할 수도 있다.
결과는,
3초 뒤 5가 출력된다.

이렇게 promise를 chaninig 했을 때는 에러의 핸들링이 굉장히 중요하다.

닭을 받아오고 닭에게 달걀을 얻고 요리하는 기능을 만들어보자. 우선 각 기능별 prommise가 필요하다.

const getHen = () =>				//닭 받아오기
	new Promise((resolve, reject) => {
    	setTimeout(() => resolve('닭'), 1000);
    });
const getEgg = hen =>				//달걀 받아오기
	new Promise((resolve, reject) => {
    	setTimeout(() => resolve(`${hen} => egg`), 1000);
    });
const cook = egg =>					//요리하기
	new Promise((resolve, reject) => {
    	setTimeout(() => resolve(`${egg} => food`), 1000);
    });


getHen()
  .then(hen => getEgg(hen))  
  .then(egg => cook(egg))
  .then(food => console.log(meal));

이때 받아오는 value가 하나의 다른 함수인 경우 .then(GetEgg)로 간결히 작성할 수 있다.

getHen()
	.then(getEgg)
    .then(cook)
    .then(console.log);
    .catch(console.log);

달걀이 떨어졌을 때 다른 재료를 받아올 수 있게 해보자.

getHen()
	.then(getEgg)
    .catch(error => {
    	return 'bread';
        })

then(getEgg) getegg를 했다면 egg를 전달하지만
아닐 경우 error를 catch 하고 계란 대신 빵을 리턴해준다.
이런 식으로 promise를 연결해 chain을 만들고 에러를 핸들링하면 콜백 지옥보다 훨씬 보기 간결하고 유지보수성도 높은 코드를 완성할 수 있다.

profile
Today I Learn # 개인 기록용 velog

0개의 댓글