본문 바로가기
JavaScript

자바스크립트 기초(ES5+)_비동기의 꽃 JavaScript async와 await / 유용한 Promise APIs

by junvely 2022. 3. 30.

 

[ async · await ]

 

  • Promise를 조금 더 간편, 간결하게 동기적으로 실행되는 것처럼 보이도록 도와주는 것 > 깔끔하게 Promise 사용하는 법
  • Promise chaining(.then)을 계속 사용하면 코드가 난잡해지는 것을 좀 더 간편한 API인 async와 await을 이용하여 동기식으로 코드를 순서대로 작성한 것처럼 작성할 수 있도록 도와준다.
  • 기존에 존재하는 Promise 위에 간편한 API를 제공 한다. > *syntactic sugar(ex : Class > 프로토타입을 베이스로하여 그 위에 살짝 덧붙여진 것)
  • Promise를 무조건 대체x > async를 사용할 경우에 따라 더 깔끔한 경우에 사용한다.
 

*syntactic sugar : 문법에 설탕을 뿌려 더 달콤하게 사용한다는 의미로 쉽고 간편하게 사용하도록 해준다.

 


 
1. async
 
  • 함수 앞에 async를 붙이면 new Promise를 쓰지 않아도 자동적으로 함수 안의 코드블럭 안이 Promise로 변환된다.
  • async 키워드를 추가하면 반환되는 return 값은 Promise(fulfil > 데이터를 받는데 성공)가 된다.
// async
// 함수 앞에 async를 붙이면 new Promise를 쓰지 않아도 자동적으로 함수 안의 코드블럭 안이 Promise로 변환된다.
async function fetchUser() {
  // 네트워크에 요청하여 받아오는 시간 10초 >>  동기적 실행일 경우 10초 후 >  return
  // 언제 데이터를 받아올지 모르겠지만 promise(약속) > .then이라는 콜백함수만 등록해 놓으면, 등록한 콜백함수를 데이터가 준비되는 대로 실행시키기로
  return "ellie";
}

const user = fetchUser(); // 비동기 처리를 하지 않으면 사용자 정보를 받아오는데 10초가 걸리기 때문에 뒤에 코드들이 읽히지 않음 > 사용자는 10초동안 텅 빈 화면 > 이렇게 오래걸리는 경우 비동기적으로 처리해줘야 한다.
user.then(console.log);
console.log(user);

 

2. await

  • await = async가 붙은 함수 안에서만 사용가능하다. > await = delay가 끝날 때까지 기다려 준다.
  • await은 Promise가 fulfil될 때 까지 잠시 중단하고, 결과를 반환한다. 그리고 실행을 기다리는 다른 코드들을 중지시키지 않고 그대로 실행되게 한다(데이터를 받는데 오래걸릴 경우 비동기적으로 처리하는 이유).
  • await 키워드는 웹 API를 포함하여 Promise를 반환하는 함수를 호출할 때 사용할 수 있다.
// await
// 1. 1초 후에 사과와 바나나 가져오는 함수 생성
function delay(ms) {
  return new Promise((resolve) => setTimeout(resolve, ms));
}

async function getApple() {
  await delay(1000); // await = async가 붙은 함수 안에서만 사용가능하다. > await = delay가 끝날 때까지 기다려 준다.
  //   throw "error";
  return "🍎"; // 3초 뒤에 사과를 리턴하는 promise가 만들어진다.
}

async function getBanana() {
  await delay(1000);
  return "🍌";
}
// promise처럼 만들기 > chaining보다 위와 같이 동기적으로 코드를 쓰는 것처럼 만들면, 쉽게 이해 가능
// function getBanana(){
//     return delay(3000)
//     .then(()=>'🍌')
// }

// 2. 사과와 바나나 모두 가져오는 함수 생성
// 콜백 지옥과 유사한 promise 중첩 chaining
function pickFruits() {
  return getApple().then((apple) => {
    return getBanana().then((banana) => `${apple} + ${banana}`);
  });
}
// async 사용하여 깔끔하게 정리하기
async function pickFruits() {
  const applePromise = getApple(); // promise 생성과 동시에 함수가 실행이 된다.
  const bananaPromise = getBanana();
  const apple = await applePromise; // 병렬적으로 사과와 바나나 모두 1초만에 실행 가능하다.
  const banana = await bananaPromise;
  return `${apple} + ${banana}`;
}

pickFruits().then(console.log);

 

3. Promise APIs 사용하여 간단하게 정리하기

// 3. Promise애서 제공하는 APIs를 사용하여 위 코드를 더 간단하게 정리하기
// Promise.all()
function pickAllFruits() {
  // Promise.all() > API > Promise배열을 전달하게 되면 모든 Promise들이 병렬적으로 다 받을 때까지 모아준다.
  return Promise.all([getApple(), getBanana()]).then(
    // 배열을 전달 > 다 받아지면, .then 다 받아진 배열이 다시 전달된다.
    (fruits) => fruits.join("+") //배열을 string으로 묶어준다(join>배열API)
  );
}
pickAllFruits().then(console.log);

// 어떤 것이든 상관없이 먼저 따지는 첫번째 과일 받아오기
// Promise.race()
function pickOnlyOne() {
  return Promise.race([getApple(), getBanana()]); // 배열에 전달된 promise 중에서 가장 먼저 값을 return하는 것만 전달된다.
}

pickOnlyOne().then(console.log); // 바나나 먼저 출력됨

 

4. 지난 시간에 콜백지옥 > Promise로 변환한 것을 > Promise API와 async await 사용하여 더 간단하게 정리하기

"use strict";

// 콜백 지옥 > promise 
class UserStorage {
  delay(ms) {
    return new Promise((resolve) => setTimeout(resolve, ms));
  }
  async loginUser(id, password) {
    await this.delay(2000);
    if (
      (id === "ellie" && password === "dream") ||
      (id === "coder" && password === "academy")
    ) {
      return id;
    } else {
      throw new Error("not found");
    }
  }

  async getRoles(user) {
    await this.delay(1000);
    if (user === "ellie") {
      return { name: "ellie", role: "admin" };
    } else {
      throw new Error("no access"); // user > coder일 경우 정보없음
    }
  }
}
const userStorage = new UserStorage();
const id = prompt("enter your id");
const password = prompt("enter your password");

async function login() {
  const user = await userStorage.loginUser(id, password); // return id 하는 promise 정보 담기 >loginUser() 바로 실행하면 그 정보를 바탕으로
  const userInfo = await userStorage.getRoles(user); // 객체를 return하는 user라는 promise 정보 담기 >getRoles() 바로 실행
  return alert(`Hello ${userInfo.name}, you have a ${userInfo.role} role`); // 정보들을 바탕으로 return하는 promise가 된다.
}

login().then(console.log).catch(console.log);

 

 

*본 포스팅은 드림코딩 유튜브강의를 정리한 내용입니다.