Doby's Lab

[자바스크립트/JavaScript] 비동기처리 (Part 4), async와 await에 대해 본문

카테고리 없음

[자바스크립트/JavaScript] 비동기처리 (Part 4), async와 await에 대해

도비(Doby) 2021. 9. 6. 03:21

async와 await이란 Promise가 Call-Back을 간결하게 표현할 수 있게 해 줬었다면 async와 await은 보다 간결해진 비동기 처리 Promise를 더 간결하게 처리해주고, 동기적으로 작성한 것처럼 보이게끔 해주는 API이다. 

기존의 Promise는 Chaining이 가능하여 코드를 지저분하게 만들 수 있지만 async와 await을 통하여 이를 간결하게 보여준다.


async

function fetchUser() {
// do network request in 10 secs...
    return 'ellie';
}

const user = fetchUser();
console.log(user);

다음 코드의 실행은 user라는 변수에 fetchUser를 할당함으로써 10초가 걸리게 되고, 10초 후에 console.log를 통해 user(받아온 데이터)를 출력한다.

 

이것이 어떠한 문제를 일으킬 수 있는가?

비동기 처리를 하지 않았을 때 저 다음 코드에도 코드들이 있다고 가정하자.

어떠한 웹페이지를 열었을 때 데이터를 받아오는 것이 10초가 걸려서 다음 코드들은 실행되지 않고, 10초가 지난 후에 웹페이지를 구성하는 요소들이 페이지에 뜨기 시작한다면 사용자들은 UI나 UX 측면에 있어서 불편함을 겪을 것이다.

 

이러한 불편함을 해소하기 위해 전 포스팅에서 Promise를 다루었었다.

위 코드를 비동기 처리, Promise를 통해 표현하면 다음과 같다.

function fetchUser() {
    return new Promise((resolve, reject) => {
    	// do network request in 10 secs...
        resolve('ellie');
    })
}

const user = fetchUser();
user.then(console.log);

 

하지만 async를 통해 이를 더 간결하게 처리할 수 있다.

async를 function 앞에 붙여줌으로써 Promise와 같은 수행을 한다.

코드는 다음과 같다.

async fetchUser() {
// do network request in 10 secs...
    return 'ellie';
}

const user = fetchUser();
user.then(console.log);

 

fetchUser는 Promise를 리턴하고, Promise에서 쓰이는 API .then()을 보아 단지 async를 붙여줌으로써 Promise가 더 간결하게 표현된 것을 알 수 있다.

즉, async에서의 return은 Promise의 resolve와 같다고 볼 수 있다.


await

await은 async가 사용된 함수 안에서만 사용할 수 있다.

await이 어떤 것이라고 말하기보다 다음 코드를 보면 설명이 될 수 있다.

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

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

 

delay라는 함수는 ms라는 매개변수가 주어지면 ms동안 setTimeout을 통해 delay 되었다가 Promise를 리턴한다.

Promise를 리턴했기 때문에 .then을 통하여 바나나를 리턴할 수 있다.

>> 즉, getBanana라는 함수는 3초간 delay가 되었다가 바나나를 리턴하는 함수이다.

 

이 코드를 await을 통해 나타낸다면 다음과 같다.

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

async function getBanana() {
    await delay(3000);
    return '🍌';
}

await이 앞에 붙은 코드가 실행되기까지 기다렸다가 다음을 실행시킨다는 뜻으로 받아들였다.

(시간 지연을 시켜준다라는 의미는 아닌 거 같다.)

 

async와 await은 Promise의 표현을 간결하게 해 준다고 했었다.

Promise는 chaining이 가능하기 때문에 밑에 작성된 코드의 함수 pickFruits를 보면 코드가 난잡함을 알 수 있다.

이를 async와 await을 통해 처리해준다.

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

async function getApple() {
    await delay(2000);
    return '🍎';
}

async function getBanana() {
    await delay(1000);
    return '🍌';
}

function pickFruits() {
    return getApple()
        .then(apple => {
            return getBanana()
                .then(banana => `${apple} + ${banana}`);
        });
}

 

pickFruits를 async와 await으로 표현해주면 다음과 같다.

async function pickFruits() {
    const apple = await getApple();
    const banana = await getBanana();
    return `${apple} + ${banana}`;
}

위 코드에서의 pickFruits보다 더 간결해진 것을 알 수 있다. 위 코드에서는 apple을 받아오면 banana를 받아오고, 그 다음 .then()을 통해 최종적으로 apple과 banana를 받아왔었다.

await을 통해 표현함으로써 apple을 받아오기까지 기다리고, banana를 받아오기까지 기다렸다가 최종적으로 return을 통해 apple과 banana를 받아왔다.

 

async와 await에서 에러는 어떻게 핸들링할까?

예를 들어 getApple()에서 에러가 났다고 가정하자. 그럴 땐 Promise처럼 try와 catch를 사용한다.

async function pickFruits() {
	try{
    	const apple = await getApple();
    	const banana = await getBanana();
    	return `${apple} + ${banana}`;
    } catch{
    	//에러 처리
    }
}

async와 await의 병렬적 처리

async function pickFruits() {
    const apple = await getApple();
    const banana = await getBanana();
    return `${apple} + ${banana}`;
}

이 코드의 실행을 살펴보면

  1. getApple()에서 3초
  2. getBanana()에서 3초

총 6초가 걸린다. 이는 비효율적이다. 병렬적 처리를 통해 3초로 줄여줄 수가 있다.

 

async function pickFruits() {
    const applePromise = getApple();
    const bananaPromise = getBanana();
    const apple = await applePromise;
    const banana = await bananaPromise;
    return `${apple} + ${banana}`;
}

Promise는 선언되자마자 실행된다는 특성을 이용하여 다음과 같이 병렬적으로 처리하여 최종적으로 3초가 걸릴 수 있다.

단, 서로가 독립적으로 실행되는 함수들끼리 병렬적으로 처리할 수 있다.

 

다만 이런 병렬 처리도 Promise API를 통해 간단하게 바꿀 수 있다.

  • Promise.all() = 모든 Promise를 한 번에 병렬적으로 처리한다.
  • Promise.race() = 모든 Promise 중에 제일 먼저 처리된 것을 가져온다.

참고

이번 비동기 처리 포스팅들은 모두 자바스크립트를 배우면서 포스팅하게 되었다.

다음 자료들을 공부하면서 정리하려는 느낌으로 포스팅들을 작성했다.

https://www.youtube.com/watch?v=s1vpVCrT8f4&t=729s

https://www.youtube.com/watch?v=JB_yU6Oe2eE 

https://www.youtube.com/watch?v=aoQSOZfz3vQ 

 

728x90