1. async, await 기본 개념

  • Promise를 대체하는 기능이 아님.
  • 코드 유지보수가 편하게 보이는 문법을 다르게 해주는 것.
// 프로미스 객체 반환 함수
function delay(ms) {
  return new Promise(resolve => {
    setTimeout(() => {
      console.log(`${ms} 밀리초가 지났습니다.`);
      resolve()
    }, ms);
  });
}

기존 Promise.then() 방식은,

function main() {
  delay(1000)
      .then(() => {
        return delay(2000);
      })
      .then(() => {
        return Promise.resolve('끝');
      })
      .then(result => {
        console.log(result);
      });
}

// 메인 함수 호출
main();

이런 식으로 delay 함수를 부를 때, then 때문에 가독성이 떨어지게 됨.

  • 별개로, 두 번째 .then()에서 return Promise.reseolve('끝');은 프로미스가 아니라 '끝'을 리턴함.
  • return '끝';과 같은 일을 수행함.
  • 굳이 저렇게 쓴 이유는 명확성과 일관성 때문임.
  • 하지만 직접 값을 반환하는 것이 더 간결하고 직관적일 수도 있다.

async, await 방식은,

async function main() {
  await delay(1000);
  await delay(2000);
  const result = await Promise.resolve('끝');
  console.log(result);
}

// 메인 함수 호출
main();

가독성이 좋음.

  • Promise 객체를 반환하는 비동기 함수가 완료되면 .then이 불리는데, await를 해서 그 이후 코드가 동작하도록 구성할 수 있음.
  • await를 하는 함수는 async 함수로 만들어야 함.
  • async 키워드를 붙인 함수는 어떤 값을 리턴해도 무조건 프로미스 객체로 감싸져서 반환함.
  • return하지 않아도, undefined를 감싼 fulfilled 프로미스 객체가 리턴됨.

async 붙여야 하는 이유는?

  • async function은 비동기로 호출되어야 하는 함수임.
  • 그 안에서 일련의 데이터 처리는 .then()과 같이 동기적으로 이루어져야 함.
  • async 함수가 불린 context에서 봤을 때 해당 함수는 비동기적으로 수행됨.
  • 하지만 async 함수 내부의 비동기적인 함수들은 동기적으로 처리되어야 하기 때문에 await를 붙임.

2. async, await 사용 예시

JAVASCRIPT
function getApple(){
  return new Promise( (resolve, reject) => {
    setTimeout(() => resolve("apple"), 1000);
  })
}

function getBanana(){
  return new Promise( (resolve, reject) => {
    setTimeout(() => resolve("banana"), 1000);
  })
}
  • 위 함수는, 프로미스 객체를 반환하는 함수.
  • 실제 상황에서 프로미스 객체를 반환하는 이유는, 내부에서 비동기적 로직이 동작하기 때문임.
  • 비동기적 로직이 완료된 경우에, Promise의 상태가 변화할 것임.
  • 우리는 이 함수를 호출한 context에서, 해당 Promise 상태 변화에 따라 추가적인 작업을 수행할 것임.

적절하지 않은 사용

async function getFruites(){
  console.time();
  let a = await getApple(); // 1초 소요
  let b = await getBanana(); // 1초 소요
  console.log(`${a} and ${b}`);
  console.timeEnd();
}

getFruites();
  • 이렇게 하면? 총 2초를 기다림
  • getApple과 getBanana는 서로 연관이 없다. 서로 기다릴 필요가 없다.

적절한 사용

async function getFruites(){

  let getApplePromise = getApple(); // async함수를 미리 논블록킹으로 실행한다. 
  let getBananaPromise = getBanana(); // async함수를 미리 논블록킹으로 실행한다. 
  
  // 이렇게 하면 각각 백단에서 독립적으로 거의 동시에 실행되게 된다.
  console.log(getApplePromise)
  console.log(getBananaPromise)
  
  let a = await getApplePromise; // 위에서 받은 프로미스객체 결과 변수를 await을 통해 꺼낸다.
  let b = await getBananaPromise; // 위에서 받은 프로미스객체 결과 변수를 await을 통해 꺼낸다.
  
  console.log(`${a} and ${b}`); // 본래라면 1초+1초 를 기다려야 하는데, 위에서 1초기다리는 함수를 바로 연속으로 비동기로 불려왔기 때문에, 대충 1.01초만 기다리면 처리된다.
})
  • getApple, getBanana에서 Promise 객체가 반환됨.
  • 둘 다 1초 뒤에 resolvee됨.
  • 1초 뒤, let a = await getApplePromise; 여기서 await가 잠에서 깨어남
  • 만약 이 부분을 .then()으로 똑같이 바꾼다면,
    let a;
    getApple()
        .then(result => {
          a = result;
        });
    이렇게 해야 함.
  • await는 프로미스에서 값을 꺼냄. 따라서 a 값이 확정됨.
  • 이미 1초를 기다린 상황이라 getBananaPromisee도 잠에서 깨어남
  • 바로 b 값이 확정되고, 아래 console log가 출력됨

3. Promise.all 메서드

  • 대부분 실무에서 사용하는 방법.
  • 모든 프로미스 비동기 함수들이 resolve되야 결과를 리턴함.
  • 제각각 비동기 함수들은 논블록킹으로 실행되어, 시간을 단축할 수 있음.
async function getFruites(){
  console.time();
  
  // 구조 분해로 각 프로미스 리턴값들을 변수에 담는다.
  let [ a, b ] = await Promise.all([getApple(), getBanana()]); 
  console.log(`${a} and ${b}`);
  
  console.timeEnd();
}

getFruites();

출처

https://inpa.tistory.com/entry/JS-%F0%9F%93%9A-%EB%B9%84%EB%8F%99%EA%B8%B0%EC%B2%98%EB%A6%AC-async-await

profile
handsome

0개의 댓글

Powered by GraphCDN, the GraphQL CDN