tech.junha.kr

노트/JavaScript

async, await

작성 수정

  • Promise Chaining을 동기적인 코드인 것 처럼 만들어준다.
  • syntatic sugar
    • 코드의 가독성과 작성 편의성을 높이기 위해 제공되는 문법적 도구
    • 실제 기능이나 동작에는 변함을 주지 않으면서도 기존 기능을 더 직관적이고 간결하게 사용할 수 있게 해준다.

1. async 함수

  • 항상 Promise 를 리턴한다.
    • FULFILLED: async 함수에서 오류 없이 끝날 경우
    • REJECTED: async 함수에서 에러가 나거나, Promise.reject()를 호출하는 경우
async function fetchUser() {
    // do network request in 10secs
    return 'junha';
}
const user = fetchUser(); // == Promise.resolve('junha')
console.log(user);

2. await 키워드

await는 Promise를 반환하는 표현식 앞에 사용한다. 해당 Promise가 처리(resolve)될 때까지 기다린 후 그 결과값을 반환한다.

  1. await는 해당 Promise가 완료될 때까지 다음 코드로 넘어가지 않게 한다.

    async function example() {
      console.log('Before');
      await new Promise(resolve => setTimeout(resolve, 1000));
      // Promise 객체가 생성됨
      // 1초 후에 해당 Promise는 resolved 상태가 됨
      console.log('After 1 second');
    }
  2. await는 반드시 async function 안에서만 사용할 수 있다.

    async function fetchData() {
      const data = await fetch('https://api.example.com/data');
      console.log(data);
    }
  3. Promise를 반환하는 함수 앞에 await를 붙인다.

    async function getUser() {
      const user = await fetchUser();  // fetchUser()의 반환 값은 Promise 객체
      console.log(user);
    }
  4. await로 받은 값은 Promise 자체가 아닌, resolved 값이다.

    async function getNumber() {
      const numberPromise = Promise.resolve(42);
      const number = await numberPromise;  // number는 42
      console.log(number);  // 출력: 42
    }

예시) async/await을 쓰지 않고 promise then만 쓰는 경우

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

function getApple() {
    return delay(2000).then(() => '🍎');
}

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

function pickFruits() {
    return getApple().then((apple) => {
        return getBanana().then((banana) => `${apple} + ${banana}`);
    }); // this is hard to read
}

pickFruits().then(console.log);

예시) async/await을 사용하여 동기적으로 표현한 경우

async function pickFruits() {
    const apple = await getApple(); // 2초 소모
    const banana = await getBanana(); // 1초 소모
    return `${apple}+${banana}`; // 총 3초 소모
}

pickFruits().then(console.log);
  • promise보다 간결하고 직관적으로 표현할 수 있다.
async function effPickFruits() {
    const applePromise = getApple(); // execute immediately
    const bananaPromise = getBanana(); // execute immediately
    const apple = await applePromise; // 2초 이하 소모
    const banana = await bananaPromise; 
    // applePromise를 기다릴 때 이미 리턴 되어있기 때문에 바로 넘어간다.
    return `${apple}+${banana}`; // 총 2초 소모
}
effPickFruits().then(console.log);

3. promise 코드 => async/await 코드

Before

function displayUser(){
    fetchUser()
    .then((user)=>{
        fetchProfile(user)//
        .then((profile))=>{
            updateUI(user,profile)
        }
    })
}

After

async function displayUser() {
    const user = await fetchUser();
    const profile = await fetchProfile(user);
    updateUI(user, profile);
}

4. 비동기 코드를 동기처럼 작동하게 하는 예시

Before

function timer(time) {
  return new Promise(function (resolve) {
    setTimeout(function () {
      resolve(time);
    }, time);
  });
}

timer(1000).then(() => console.log('then 실행'));
console.log('나는 어디있을까');

// 나는 어디있을까
// (1초 후)
// then 실행

After

async function init() {
  await timer(1000).then(() => console.log('then 실행'));
  console.log('나는 어디있을까');
}

function timer(time) {
  return new Promise(function (resolve) {
    setTimeout(function () {
      resolve(time);
    }, time);
  });
}

init()

// (1초 후)
// then 실행
// 나는 어디있을까
  • 비동기 코드를 동기처럼 작동하게 하는 마법은 없다,
  • 동기 코드를 비동기 코드 블럭안에서 실행시켜야한다.

5. try catch in async/await

  • 비동기 코드의 에러가 발생했을 경우 try-catch 구문으로 처리할 수 있다.
async function fetchData() {
  try {
    const response = await fetch('https://api.example.com/data');
    
    // HTTP 응답 상태가 200이 아닌 경우 에러를 던짐
    if (!response.ok) {
      throw new Error('Network response was not ok');
    }

    const data = await response.json();
    console.log('Data:', data);
  } catch (error) {
    // 에러 처리
    console.error('Error fetching data:', error.message);
  }
}

fetchData();
  • await로 비동기 요청을 처리할 때 에러가 발생할 수 있다.
  • 이런 경우에는 try 블록으로 감싸서 에러를 감지하고 catch 블록에서 처리한다.
  • 네트워크 오류나 응답 상태에 따른 예외 처리도 가능하며, 모든 종류의 에러를 잡을 수 있다.