JavaScript - Async · Await

Noma·2021년 1월 30일
0

1) async

: Promise를 좀더 간편하고 간결하게 해주면서 동기적으로 실행되어 보이게 해주는 syntatic sugar이다.

*syntactic sugar: 프로그래밍 언어 차원에서 제공되는 것으로 기존의 코드를 더 논리적으로 간결하게 표현해주는 것이다.

예를 들어,

function fetchUser(){
	return new Promise((resolve,reject)=>{
    	resolve('merry');
    })
};

위 코드는 아래와 같다.

async function fetchUser(){
	return 'merry';

async를 function 앞에 작성하면 번거롭게 promise 구문을 다 쓰지 않아도 자동으로 함수 안에 있는 코드 블럭이 Promise로 변환된다.

❗ 참고
return을 하면 상태가 fulfilled가 되고 throw를 하면 상태가 rejected가 된다.
이를 확인하려면 Promise를 실행한 결과를 변수에 할당해 변수를 출력해봐야함

- Description

async 함수는 Promise를 반환한다.

function delay(ms){
    return new Promise(resolve=>setTimeout(resolve,ms));
}
//정해진 밀리세컨드가 지나고 resolve를 호출하는 Promise를 리턴

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

아래의 코드와 같다.

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

2) await

await 키워드는 async 함수에서만 유효하다. async 함수의 본문 외부에서 사용하면 SyntaxError가 발생한다.

- Description

await은 async 함수의 실행을 일시 중지하고 전달된 Promise의 해결을 기다린 다음 async 함수의 실행을 다시 시작하고 완료 후 값을 반환한다.
이때 await문의 반환값은 Promise에서 fulfill된 값이 된다.
만약 Promise가 reject되면, await문은 reject된 값을 throw한다.
await 연산자 다음에 나오는 문의 값이 Promise가 아니면 해당 값을 resolved Promise로 변환시킨다.

function delay(ms){
    return new Promise(resolve=>setTimeout(resolve,ms));
}
//정해진 밀리세컨드가 지나고 resolve를 호출하는 프로미스를 리턴

async function getApple(){
    await delay(2000);
    return '🍎';
} 
//await은 delay가 끝날 때까지 기다려줌
//2초 기다렸다가 사과 리턴

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

다음 코드처럼 Promise를 너무 중첩해서 체이닝을 하게 되면 콜백지옥과 비슷한 문제점 발생한다.

function pickFruits(){
    return getApple().then(apple=>{
        return getBanana().then(banana=> `${apple} + ${banana}`);
    });
}
//
pickFruits().then(console.log);
[console]
//3초 후

🍎 + 🍌

그러므로, 위의 코드를 async/await으로 표현하면 다음과 같다.

async function pickFruits(){
    const apple=await getApple(); //2초 기다려
    const banana=await getBanana(); //1초 기다려
    return `${apple}+${banana}`; //총 3초 후 리턴
}
pickFruits().then(console.log);

한결 보기 편해졌다. 하지만 위의 경우, 바나나와 사과를 받아오는 데 서로 연관이 없으므로 서로를 기다릴 필요가 전혀없다. 따라서 이렇게 순차적으로 진행되면 비효율적이므로, 프로미스가 만들어지자마자 executor가 실행되는 특성 이용해 병렬적으로 처리하는 것이 좋다.

에러 처리는 마지막에서 다룬다..

3) Parallel Processing

async function pickFruits(){
    const applePromise=getApple();
    const bananaPromise=getBanana();
  //사과와 바나나 동시에 카운트 시작
    const apple=await applePromise;
    const banana=await bananaPromise;
    return `${apple}+${banana}`;
}
pickFruits().then(console.log);

이렇게 하게 되면, 두 타이머가 모두 생성된 다음 await을 하게 되므로 3초(2초+1초)가 아닌 2초가 걸리게 된다. 그러나 await 호출은 여전히 연속적으로 실행되므로 두번째 await은 첫 번째 호출이 끝날 때까지 대기한다.

4) Promise APIs

위의 코드를 더욱 간결하게 하려면, promise가 제공하는 API인 .all을 이용하면 된다.

- Promise.all()

: Promise 배열을 전달해주면 모든 Promise들이 병렬적으로 다 받아질 때까지 모아준다. 즉 앞선 코드와 동일한 일을 수행한다.

function pickAllFruits(){
    return Promise.all([getApple(),getBanana()]).then(fruits=>fruits.join('+'));
}
pickAllFruits().then(console.log);

또 다른 API로는,

- Promise.race()

배열로 전달된 Promise 중에서 가장 먼저 값을 리턴하는 것만 전달되어지는 api이다.

function pickOnlyOne(){
    return Promise.race([getApple(),getBanana()]);
};
pickOnlyOne().then(console.log);
[console]

🍌

5) Error Handling

에러처리할 땐 try catch문을 이용한다. (병렬처리는 불가)

async function pickFruits(){
	try{
		const apple=await getApple();
		const banana=await getBanana();
	}catch(error){
    throw error; //~~ 예외 처리 ~~
	}finally{
		return `${apple}+${banana}`;
	}	
}
pickFruits().then(console.log).catch(console.log);

- throw

: throw문은 사용자가 정의한 예외를 발생시킨다. 현재 실행되고 있던 함수는 중지되고(throw이후의 코드는 실행되지 않음), control이 콜스택의 첫 번째 catch 블록으로 전달된다. caller 함수 사이에 catch 블록이 없으면 프로그램은 종료된다.

❗ 참고자료
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/async_function
https://www.youtube.com/channel/UC_4u-bXaba7yrRz_6x6kb_w

profile
Frontend Web/App Engineer

0개의 댓글