: 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를 실행한 결과를 변수에 할당해 변수를 출력해봐야함
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 '🍌';
}
await 키워드는 async 함수에서만 유효하다. async 함수의 본문 외부에서 사용하면 SyntaxError가 발생한다.
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가 실행되는 특성 이용해 병렬적으로 처리하는 것이 좋다.
에러 처리는 마지막에서 다룬다..
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
은 첫 번째 호출이 끝날 때까지 대기한다.
위의 코드를 더욱 간결하게 하려면, promise가 제공하는 API인 .all
을 이용하면 된다.
: Promise 배열을 전달해주면 모든 Promise들이 병렬적으로 다 받아질 때까지 모아준다. 즉 앞선 코드와 동일한 일을 수행한다.
function pickAllFruits(){
return Promise.all([getApple(),getBanana()]).then(fruits=>fruits.join('+'));
}
pickAllFruits().then(console.log);
또 다른 API로는,
배열로 전달된 Promise 중에서 가장 먼저 값을 리턴하는 것만 전달되어지는 api이다.
function pickOnlyOne(){
return Promise.race([getApple(),getBanana()]);
};
pickOnlyOne().then(console.log);
[console]
🍌
에러처리할 땐 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이후의 코드는 실행되지 않음), 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