TIL39, JS: Promise 가 mojo?

sunghoonKim·2021년 1월 27일
1

프로미스 공부 더 이상 미룰수 없다. 정리한다.


비동기 vs 동기

자꾸 헷갈리는 개념인데 비동기적이라 함은, 특정 코드의 연산이 끝날때까지 기다린 후 다음 코드를 실행하는 것이 아니라, 기다리지 않고 바로 다음 코드를 실행하는 것을 말한다.

그와 반대로, 동기적이라 함은, 그 반대의 의미로, 특정 코드의 연산이 끝난 후에, 다음 코드를 실행하는 것.


프로미스가 왜 필요할까.

예로, 데이터를 받아오는 작업을 생각해보자. 만약 통신을 하는데 서버에서 응답을 받는데 10초 정도가 걸린다고 할때, 이를 동기적으로 처리할 시 10초 동안 다른 작업을 하지 못하고 응답을 받을 때까지 기다려야 할 것이다. 만약 여러 번의 통신이 이루어져야 한다면, 실제로 서비스를 이용하는 시간보다 서버에서 응답을 기다리는 시간이 훨씬 긴 지옥같은 ux가 일어날 것.

그러한 부분을 비동기 처리 하여 기다리지 않고 다음 코드로 넘어갈 수 있도록 해줄 수 있는데, 그때 사용 하는 것이 promise 이다.

Promise 객체는 비동기 작업이 맞이할 미래의 완료 또는 실패와 그 결과 값을 나타냅니다.

promise 객체는 미래의 받을 데이터가 담길 컨테이너의 역할을 한다. 따라서, 비동기 처리를 할 함수에서 결과 값을 프로미스에 담고, 해당 프로미스 객체를 반환해주면 된다. 이는 즉, "난 일단 계속 갈껀데, 나중에 결과 값을 받으면 프로미스에 담아줘" 라고 말해주는 것과 같다. 이것을 통해서 동기적인 자바스크립트에서 비동기 처리를 해줄 수 있다.

대표적인 비동기 함수가 fetch / axios 인데, 이것들은 모두 promise 객체를 반환한다.


프로미스 기본 문법

프로미스 객체를 생성하는 문법은 다음과 같다.

const promise = new Promise((resolve, reject) => {
	if ( /* Condition */ ) {
		resolve( /* 성공시 반환할 값 */ );
	} else {
		reject( /* 실패시 반환할 값 */ );
	}
});

(if 문을 사용했지만, 어떠한 로직이 들어와도 상관 없다.)

비동기적으로 처리 할 함수에서 위의 프로미스 객체를 생성하고, 반환해 주면 된다.

const functionToBeAsynced = () => {
	const promise = new Promise((resolve, reject) => {
	    if ( /* Condition */ ) {
		    resolve( /* 성공시 값 */ );
	    } else {
		    reject( /* 성공시 값 */ );
	    }
	});
	return promise;
};

프로미스 객체에 담긴 결과 값 접근하기

이후, 프로미스에 담긴 결과 값에 접근하기 위해서, then 메소드를 사용한다. 문법은 아래와 같다.

p.then(onFulfilled[, onRejected]);

functionToBeAsynced().then((res) => {
  /* Do Something Here! */
  console.log(res);
});

Rejected / Error 핸들링

에러를 핸들링 하는 방법은 2가지가 있는데,

  1. then 메소드의 2번째 인자에 콜백 함수를 넘기는 것
  2. catch 메소드를 체인 하는 것이 있다.

1번 방법.

functionToBeAsynced().then(
  (successPayload) => {
    /* do something here if resolved */
    console.log(successPayload);
  },
  (errorPayload) => {
    /* do something here if rejected */
    console.log(errorPayload);
  }
);

2번 방법.

const myPromise = new Promise((resolve, reject) => {
  resolve('successPayload');
  // reject( 'errorPayload' );
});
myPromise.then(successCallback).catch(errorCallback).finally(finallyCallback);

위에서 finally 메소드 또한 체이닝이 됬는데, finally 메소드는 promise의 상태가 fullfilled 되었을 때(resolved 혹은 rejected) 실행된다.


async / await

async / await프로미스에 대해서 사용할 수 있는데, 프로미스에 담기는 결과 값을 좀 더 직관적인 방법으로 가져올 수 있도록 해준다. 그리고 그것 덕분에 코드를 더 가독성 있게 작성할 수 있다.

용법은 아래와 같다.

const increase = (number) => {
  return new Promise((resolve, reject) => {
    setTimeout(()=>{
      const result = number + 10;
      if (result >= 50) {
        const e = new Error('Number too big!');
        reject(e);
      }
      resolve(result);
    }, 1000);
  });
}

const test = async () => {
  try {
    const number = await increase(10);
    console.log("number: ", number);
    const number2 = await increase(20);
    console.log("number2: ", number2);
    const number3 = await increase(50);
    console.log("number3: ", number3);
  }
  catch{
    console.log('에러남!');
  }
}

test();

1초 후에 number 가 출력되고,
2초 후에 number2 가 출력되고,
3초 후에 에러남 메시지가 출력된다.

aync 를 사용할 함수 앞에 명시해주고, 연산을 기다릴 함수 앞에 await 붙여주는 것으로 비동기 함수들을 순차적으로 실행할 수 있다. 보다시피 프로미스 안에 담기는 값에 접근하기 위해, 기존에는 then 메소드 안 콜백함수를 통해야 했지만, await 를 통하면 값을 직접적으로 꺼내 바로 변수에 담을 수 있다.

주의 해야될 점은, async 를 붙일 시 해당 함수는 리턴 값이 무엇이 되든, 프로미스 객체를 반환한다.

const increase = (number) => {
  return new Promise((resolve, reject) => {
    setTimeout(()=>{
      const result = number + 10;
      if (result >= 50) {
        const e = new Error('Number too big!');
        reject(e);
      }
      resolve(result);
    }, 1000);
  });
}

const test = async () => {
  const number = await increase(10);
  return "testString";
}

console.log(test()); // Promise 객체가 출력된다. "testString" 이 아니라.

재밌네. 프로미스.

4개의 댓글

comment-user-thumbnail
2021년 1월 28일

제목 어그로.... 대단합니다.

답글 달기
comment-user-thumbnail
2021년 1월 28일

따봉 두 개 드립니다 !! 👍👍

답글 달기
comment-user-thumbnail
2021년 1월 31일

정리 참 잘하네 이 남자

답글 달기
comment-user-thumbnail
2021년 2월 2일

많이 배우고갑니다

답글 달기