기존에 존재하는 promise 위에 조금 더 간편한 API를 제공하는 문법적 설탕(syntactic sugar)이다.
async 함수와 특징
//ex.1) result가 1인 프로미스가 반환된다.
async function f() {
return 1;
}
f().then(alert); // 1
// ex.2) 명시적으로 프로미스를 반환한다. 결과는 같다.
async function f() {
return Promise.resolve(1);
}
f().then(alert); // 1
✍️ 예제를 통해 알아보자.
function fetchUser() {
return new Promise((resolve, reject) => {
resolve('seul');
})
}
const user = fetchUser();
user.then(console.log);
// suel
fetchUser()
는 프로미스를 리턴한다.async
키워드를 사용해보자. ▼ async function fetchUser1() {
return 'seul!';
}
const user1 = fetchUser1();
user1.then(console.log);
// suel!
async
키워드를 붙이면 자동적으로 함수 안에 있는 블럭들이 promise로 변환되어진다.await 문법과 특징
//await 문법
let value = await promise;
//async와 await
async function f() {
let promise = new Promise((resolve, reject) => {
setTimeout(() => resolve("완료!"), 1000)
});
let result = await promise; // 프로미스가 이행될 때까지 기다린다.
// 프라미스 객체의 result 값이 변수 result에 할당된다.
alert(result); // "완료!" (1초 뒤에 실행)
}
f();
✍️ 예제를 통해 알아보자.
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function getApple(){
await delay(1000);
return '🍎';
}
async function getBanana() {
await delay(2000);
return '🍌';
}
getApple().then(console.log); // 1초 후 사과 출력
getBanana().then(console.log); // 2초 후 바나나 출력
✍️ 사과와 바나나를 한번에 다 가져오는 pickFruits 이라는 함수를 만들어 보자.
(1) 기존의 promise 체이닝을 사용할 경우 ▼
function pickFruits() {
return getApple()
.then((apple) => {
return getBanana()
.then((banana) => `${apple} + ${banana}`)
pickFruits().then(console.log);
// 🍎 + 🍌 (3초 후에 출력된다.)
(2) async 키워드를 이용하기 ▼
async function pickFruits() {
const apple = await getApple();
const banana = await getBanana();
return `${apple} + ${banana}`;
}
pickFruits().then(console.log);
// 🍎 + 🍌 (3초 후에 출력된다.)
await을 사용할 때 주의할 점
let response = await fetch('/article/promise-chaining/user.json');
let user = await response.json();
(async () => {
let response = await fetch('/article/promise-chaining/user.json');
let user = await response.json();
...
})();
await promise
는 프로미스 객체의 result에 저장된 값을 반환한다.throw
문을 작성한 것처럼 에러가 던져진다.throw
문은 사용자 정의 예외를 발생(throw)할 수 있다. MDN-throwasync function f() {
await Promise.reject(new Error("에러 발생!"));
}
// 위와 동일하다.
async function f() {
throw new Error("에러 발생!");
}
try..catch
를 이용한다.async function f() {
try {
let response = await fetch('http://유효하지-않은-주소');
} catch(err) {
alert(err); // TypeError: failed to fetch
}
}
f();
위에서 살펴본 🍎 + 🍌 예제도 확인해보자.
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function getApple(){
await delay(1000);
throw 'error' // 에러가 발생한다.
return '🍎';
}
async function getBanana() {
await delay(2000);
return '🍌';
}
async function pickFruits() {
try {
const apple = await getApple();
const banana = await getBanana();
} catch(err) {
alert(err); // 에러 팝업창이 뜬다.
}
return `${apple} + ${banana}`;
}
pickFruits().then(console.log);
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function getApple(){
await delay(2000);
return '🍎';
}
async function getBanana() {
await delay(2000);
return '🍌';
}
async function pickFruits() {
const apple = await getApple();
const banana = await getBanana();
return `${apple} + ${banana}`;
}
pickFruits().then(console.log);
// 🍎 + 🍌 (4초 후에 출력된다.)
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function getApple(){
await delay(2000);
return '🍎';
}
async function getBanana() {
await delay(2000);
return '🍌';
}
async function pickFruits() {
const applePromise = getApple();
const bananaPromise = getBanana();
const apple = await applePromise;
const banana = await bananaPromise;
return `${apple} + ${banana}`;
}
pickFruits().then(console.log);
// 🍎 + 🍌 (2초 후에 출력된다.)
(1) Promise.all()
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function getApple(){
await delay(1000);
return '🍎';
}
async function getBanana() {
await delay(1000);
return '🍌';
}
function pickAllFruits(){
return Promise.all([getApple(), getBanana()])
.then(fruits =>
fruits.join(' + ')
);
}
pickAllFruits().then(console.log);
// 🍎 + 🍌
all()
API는 프로미스 배열을 전달하게 되면, 모든 프로미스들이 병렬적으로 다 받을 때까지 모아준다.then
을 통해 즉, 다 받아진 배열이 다시 전달된다.join()
을 사용해서 출력해 본다.(2) Promise.race()
function delay(ms) {
return new Promise(resolve => setTimeout(resolve, ms));
}
async function getApple(){
await delay(3000); // 3초가 소요된다.
return '🍎';
}
async function getBanana() {
await delay(1000); // 1초가 소요된다.
return '🍌';
}
function pickOnlyOne(){
return Promise.race([getApple(), getBanana()]);
}
pickOnlyOne().then(console.log);
// 🍌 (바나나가 먼저 가져와진다.)
race()
API는 배열에 전달된 프로미스 중에서 가장 먼저 값을 리턴하는 것만 전달된다.function 앞에
async
키워드를 추가하면 두가지 효과가 있다.
promise 앞에
await
키워드를 붙이면 자바스크립트는 promise가 처리될 때까지 대기한다. 처리가 완료되면 조건에 따라 아래와 같은 동작이 이어진다.
async & await 그리고 then & catch
async & await을 사용하면 await가 대기를 처리해주므로 then을 쓰지 않아도 되고, catch 대신 try..catch를 사용할 수 있다는 장점도 생긴다.
보통은 then을 사용하는 것보다 async & await를 사용하는 것이 더 편리하다고 한다.
하지만 async함수 바깥의 최상위 레벨 코드에선 await를 사용할 수 없다. 그렇기 때문에 관행처럼 then & catch를 추가해 최종 결과나 처리되지 못한 에러를 다룬다고 한다. 🤔
reference
MDN-async
MDN-await
async_await
dream-coding
MDN-all