본문 바로가기
Javascript

JavaScript Async/Await (비동기 함수와 실행 순서)

by ttum 2020. 6. 27.

Promise → then → promise → then → ... 계속 반복해서 사용하면 난잡해짐!

⇒ 이 문제를 해결하고자 등장한게 Async/Await! (Syntactic sugar)

The async and await keywords enable asynchronous, promise-based behavior to be written in a cleaner style, avoiding the need to explicitly configure promise chains. (비동기적인 코드를 동기적으로 보이도록! 가독성 높아짐)

# Async

async function name([param[, param[, ...param]]]) {
   statements
}

이런식으로 앞에 async 키워드를 사용하면 된다.

이렇게 생성한 async함수는 Promise 를 리턴한다 ✨ (resolved의 결과일 수도 있고, reject의 결과일 수도 있음)

# Await

async함수 안에는 await 표현이 포함될 수 있다.

await 키워드는 promise-based의 비동기 작업이 fulfilled 되거나 rejected 될때까지 기다려줌!

하나의 비동기 작업을 promise와 async로 표현해보면 아래와 같다.

'use strict';

function waitForMe(){
  return new Promise((resolve, reject)=>{
    setTimeout(()=>{
      resolve(`Thank you for waiting me 😊`);
    }, 3000);
  });
}

// 1. promise then
function promiseWait(){
  waitForMe().then(console.log);
}

// 2. async await
async function asyncWait(){
  const res = await waitForMe();
  console.log(res);
}

promiseWait(); // Thank you for waiting me 😊
asyncWait(); // Thank you for waiting me 😊

얼핏 보면 then이 간단해보일 수 있지만, 계속해서 비동기작업을 해야된다면 Promise chain이 줄줄이 반복되어서 가독성이 떨어진다.

반면 async/await을 이용해 표현한 함수는 동기적인 코드처럼 보이기 때문에 이해하기가 쉬워진다.(syntactic sugar의 힘🍫🍬🧁)

# 조금 더 알아보기

앞에서 말했듯이 Async/Await는 Promise의 Syntactic Sugar다!

그래서 본질적으로는 둘이 똑같은것이다.

'use strict';

async function asyncWay() {
   return 1
}

function promiseWay() {
   return Promise.resolve(1)
}

console.log(asyncWay()); // Promise {<resolved>: 1}
console.log(promiseWay()); // Promise {<resolved>: 1}

둘 다 똑같이 Promise함수를 만든 것이다!😯👍

# 비동기 처리 순서

동시에 여러개의 비동기를 처리하는 방법은 여러가지가 있다.

하나의 작업이 다 완료되어야 다음 작업을 이어갈 수도 있을 것이고, 둘이 동시에 시작할 수도 있다.

 

두 가지 작업을 처리해야한다고 치자.

첫 번째 작업은 도리🐟가 밥을 먹는 것이고, 두 번째 작업은 니모🐠가 밥을 먹는 것이다.

니모는 아기라서 밥을 먹는데 5초가 걸리고 도리는 2초가 걸린다.

"use strict";

const nemoEat = function(){
    return new Promise((resolve)=>{
        setTimeout(()=>resolve(`Nemo finished meals🐠`), 5000);
    })
};

const doriEat = function(){
    return new Promise((resolve)=>{
        setTimeout(()=>resolve(`Dori finished meals🐟`), 2000);
    })
};

1. Sequential: 니모가 밥을 다 먹은 후에야 도리가 밥을 먹을 수 있다.

니모가 5초동안 밥을 다 먹은 후에야 도리가 밥을 2초동안 먹는다.

그래서 둘 다 식사를 마무리하는 데까지 총 7초가 걸린다!

console.time()과 console.timeEnd()를 이용해서 시간을 측정했다.
const sequential = async()=>{
    console.time('🐠 meal time');
    console.time('🐟 meal time');

    const nemo = await nemoEat();
    console.log(nemo);
    console.timeEnd('🐠 meal time');

    const dori = await doriEat();
    console.log(dori);
    console.timeEnd('🐟 meal time');
} 

sequential();

// result
// Nemo finished meals🐠
// 🐠 meal time: 5000.904052734375ms
// Dori finished meals🐟
// 🐟 meal time: 7003.98779296875ms

2. Concurrent: 니모랑 도리랑 밥을 같이 먹기 시작한다.

같이 먹기 시작하면 니모는 5초가 걸리고, 도리는 2초가 걸리니까 둘이 함께 식사를 마치는 데는 총 5초가 걸리게 된다. 도리는 니모가 식사를 마칠 때까지 기다렸다가 같이 식사를 마무리한다고 해보자!(니모가 아직 아기라서 챙겨줘야됨👶)

const concurrent = async()=>{
    console.time('🐠 meal time');
    console.time('🐟 meal time');

    // 동시에 밥을 먹기 시작
    const nemo = nemoEat();
    const dori = doriEat();

    console.log(await nemo);
    console.timeEnd('🐠 meal time');

    console.log(await dori);
    console.timeEnd('🐟 meal time');
}

concurrent();

// result
// Nemo finished meals🐠
// 🐠 meal time: 5002.114990234375ms
// Dori finished meals🐟
// 🐟 meal time: 5002.39501953125ms

위 코드는 Promise.all을 이용한 것과 같은 결과를 나타낸다. (마찬가지로 5초가 걸린다.)

Promise.all 한 번 더 짚고 넘어가기!
The Promise.all() method takes an iterable of promises as an input, and returns a single Promise as an output.
const concurrentWithPromiseAll = ()=>{
    console.time('meal time');

    Promise.all([nemoEat(), doriEat()])
    .then(res=>{
        console.log(res[0]);
        console.log(res[1]);
        console.timeEnd('meal time');
    });
}

concurrentWithPromiseAll();

3. ✨ Parallel: 각자 식사를 하고 각자 종료한다. ✨

이제 니모가 다 컸다! 이제 더 이상 도리가 니모의 식사를 기다려줄 필요가 없다. 각자 혼밥하고 식사를 마치면 된다.

도리는 2초만에 식사를 끝낼 수 있고, 니모는 5초동안 먹고 알아서 식사를 마치면 된다.

const parallel = ()=>{
    console.time('🐠 meal time');
    console.time('🐟 meal time');

    Promise.all([
        (async ()=>{
            console.log(await nemoEat());
            console.timeEnd('🐠 meal time');
            })(),
        (async ()=>{
            console.log(await doriEat());
            console.timeEnd('🐟 meal time');
            })()
    ])
}

parallel();


//result
// Dori finished meals🐟
// 🐟 meal time: 2001.485107421875ms
// Nemo finished meals🐠
// 🐠 meal time: 5001.932861328125ms