TIL 20 | async와 await

이사감·2021년 3월 18일
0

Javascript

목록 보기
5/9
post-thumbnail

💌 이 글은 드림코딩의 비동기 3부작 | 콜백 - 프로미스 - async & await 에서 async & await에 대해 다룹니다

async & await

앞서 콜백 지옥을 해결하기 위해 프로미스를 쓴다고 공부했다. 그렇다면 프로미스는 아무런 문제가 없는 방법일까?

💬 예제 1

function delay(ms) {
  return new Promise (resolve => setTimeout(resolve, ms));
}

function getApple() {
  return delay(1000)
  .then(() => `🍎`);
}

function getBanana() {
  return delay(1000)
  .then(() => `🍌`);
}

function pickFruits() {
  return getApple().then(apple => {
    return getBanana().then(banana => `${apple} + ${banana}`);
  }); // 🔥 콜백 지옥의 향기...
}

pickFruits().then(console.log);  // 🍎 + 🍌

위의 예제코드에서 알 수 있듯 프로미스도 여러번 중첩해서 사용하면 결국 콜백 지옥과 똑같은 문제가 발생한다. 이 문제는 asyncawait을 통해 해결할 수 있다.

  • async & await를 함께 사용하여 읽고 쓰기 쉬운 비동기 코드를 작성할 수 있다.
  • async & await를 사용하면 promise.then이 거의 필요 없다.
  • async & await는 프로미스를 기반으로 한다.

1. async

  • async를 통해 프로미스를 좀 더 편하게 사용할 수 있다. 이것은 새로운 것이 아니라, 기존의 프로미스를 베이스로 API를 추가하는 것으로, Syntactic Sugar의 일종이다.
    ✔ Class 또한 프로토타입을 베이스로 하는 것으로, Syntactic Sugar의 일종이다.

  • 프로미스를 쓰지 않아도 함수안의 코드블럭이 자동으로 프로미스로 변환된다.

  • async가 앞에 붙은 함수는 항상 프로미스를 반환한다. 프로미스가 아닌 값도 resolved promise로 감싸, resolved promise가 반환되게 한다.

1.1 async 문법

함수 앞에 async를 붙이는 아주 간단한 방법이다.

  async function f() {
    return 1;
  }

  const user = f();

  // async가 붙은 함수는 항상 프로미스를 반환한다.
  console.log(user);     // Promise {<fulfilled>: 1}

  // 호출 방법
  // 1. 변수에 할당해서 호출
  user.then(console.log); // 1

  // 2. 함수로 바로 호출
  f().then(console.log); // 1

2. await

  • 동기적인 코드를 쓰는 것처럼 만들어 promise.then보다 가독성 좋게 프로미스의 result 값을 얻을 수 있게 해준다.

  • async가 붙은 함수 안에서만 사용 가능하다.

  • 프로미스 앞에 await을 붙이면 자바스크립트는 프로미스가 처리(settled)될 때까지 기다린다.

💬 예제 2

  async function f() {

    let promise = new Promise((resolve, reject) => {
      setTimeout(() => resolve("완료!"), 1000)
    });

    let result = await promise; // 프로미스가 처리될 때까지 기다림(실행 중단)
    console.log(result); // 1초뒤 완료!가 콘솔에 찍힘
  }

  f();

💬 예제 3

  function delay(ms){
      return new Promise(resolve => setTimeout(resolve, ms))
  };

  async function getApple(){
      await delay(1000); // delay가 끝날 때까지 기다림
      return '🍎'; // delay 처리 후 '🍎'을 리턴하는 프로미스가 async에 의해 만들어짐
  }

  getApple().then(console.log);  // 🍎

// 위의 async를 promise chaining으로 표현하면?
  function getApple(){
      return delay(1000).then(()=>'🍎');
  }

💬 예제 4 | 예제 1의 promise.thenasync & await으로 바꿔보자

// 예제 1 일부
function pickFruits() {
  return getApple().then(apple => {
    return getBanana().then(banana => `${apple} + ${banana}`);
  }); // 콜백 지옥의 향기...🔥
}

pickFruits().then(console.log);  // 🍎 + 🍌

// async & await으로 변환
async function pickFruits() {
  const apple = await getApple(); 
  const banana = await getBanana();  
  return `${apple} + ${banana}`;
}

pickFruits().then(console.log);  // 🍎 + 🍌

2.1 병렬 처리

💬 예제 4 에서, getApple()getBanana()는 연관성이 없으므로 서로 기다려주지 않고 동시에 병렬적으로 실행되어도 된다. 이를 위해 프로미스를 선언하여 즉시 실행되게 만든다.

async function pickFruits() {
  const applePromise = getApple();   // 프로미스는 선언 즉시 바로 실행됨
  const bananaPromise = getBanana();
  
  // 동기화
  const apple = await applePromise; 
  const banana = await bananaPromise;
  
  return `${apple} + ${banana}`;
}

pickFruits().then(console.log);  // 🍎 + 🍌

그러나 이렇게 더럽게 작성하는 것보다, Promise.all 메소드를 사용하여 깔끔하게 작성하는 것이 좋다.

Promise.all 사용하기

  • 배열 내 모든 값의 이행(또는 첫 번째 거부)을 기다립니다. MDN
Promise.all([프로미스1, 프로미스2, 프로미스3])
  .then(values => { console.log(values);
});
function pickAllFruits() {
  // 프로미스 처리 결과가 담긴 배열을 기다린다
  return Promise.all([getApple(), getBanana()]).then(fruits => {
    return fruits.join(` + `);
  });
	// return Promise.all([getApple(), getBanana()]);
}
pickAllFruits().then(console.log);  // 🍎 + 🍌

모든 promiseasync & await으로 변환해야 할까?

그렇지 않다. 프로미스 사용에는 두 방법이 있다.

  1. 프로미스 그대로 사용하는 방법
  2. async & await으로 변환하는 방법

엘리님에 의하면, 프로젝트를 많이 해보며 두 방법의 차이점에 대해 감을 잡아야 한다고...



📚 참고자료

profile
https://emewjin.github.io

1개의 댓글

comment-user-thumbnail
2021년 3월 18일

콜백 지옥도 지옥인데.. 프로미스도 지옥이고... async도 지옥이고 ..😭😭

답글 달기