async
는 function 앞에 위치합니다.
async function func() {
return 1;
}
function앞에 async
를 붙이면 해당 함수는 항상 프로미스를 반환합니다.
프로미스가 아닌 값을 반환하더라도 이행 상태의 프로미스(resolved promise)로 값을 감싸 이행된 프로미스가 반환되도록 합니다.
아래 예시의 함수를 호출하면 result가 1인 이행 프로미스가 반환됩니다.
async function func() {
return 1;
}
func().then(alert); // 1
명시적으로 프로미스를 반환하는 것도 가능한데, 결과는 동일합니다.
async function func() {
retrun Promise.resolve(1);
}
func().then(alert); // 1
async
가 붙은 함수는 반드시 프로미스를 반환하고, 프로미스가 아닌 것은 프로미스로 감싸 반환합니다.
그런데 async
가 제공하는 기능은 이뿐만이 아닙니다. 또 다른 키워드 await
은 async
함수 안에서만 동작합니다.
await
문법은 다음과 같습니다.
// await는 async 함수 안에서만 동작합니다.
let value = await promise;
자바스크립트는 await
키워드를 만나면 프로미스가 처리될 때까지 기다립니다.(await은 '기다리다'라는 뜻을 가진 영단어입니다.) 결과는 그 이후 반환됩니다.
1초 후 이행되는 프로미스를 예시로 사용하여 await
가 어떻게 동작하는지 살펴봅시다.
async function func() {
let promise = new Promise((resolve, reject) => {
setTimeout(() => resolve("완료!"), 1000)
})
let result = await promise; // 프로미스가 이행될 때까지 기다림 (*)
alert(result); // 완료!
}
func();
함수를 호출하고, 함수 본문이 실행되는 도중에 (*)
로 표시한 줄에서 실행이 잠시 '중단'되었다가 프로미스가 처리되면 실행이 재개됩니다. 이때 프로미스 객체의 result 값이 변수 result에 할당됩니다. 따라서 위 예시를 실행하면 1초 뒤에 '완료!'가 출력됩니다.
await
은 말 그대로 프로미스가 처리될 때까지 함수 실행을 기다리게 만듭니다. 프로미스가 처리되면 그 결과와 함께 실행이 재개되죠. 프로미스가 처리되길 기다리는 동안엔 엔진이 다른 일(다른 스크립트 실행, 이벤트 처리 등)을 할 수 있기 때문에, CPU 리소스가 낭비되지 않습니다.
await
은 promise.then
보다 좀 더 세련되게 프로미스의 result 값을 얻을 수 있도록 해주는 문법입니다.
promise.then
보다 가독성 좋고 쓰기도 쉽습니다.
일반 함수엔
await
을 사용할 수 없습니다.
async
함수가 아닌데await
을 사용하면 문법 에러가 발생합니다.
function func() {
let promise = Promise.resolve(1);
let result = await = promise; // Syntax error
}
function 앞에 async
를 붙이지 않으면 이런 에러가 발생할 수 있습니다.
앞서 설명해 드린 바와 같이 await
는 async
함수 안에서만 동작합니다.
프로미스가 정상적으로 이행되면 await promise
는 프로미스 객체의 result
에 저장된 값을 반환합니다. 반면 프로미스가 거부되면 마치 throw
문을 작성한 것처럼 에러가 던져집니다.
예시:
async function func() {
await Promise.reject(new Error('에러 발생!'));
}
위 코드는 아래 코드와 동일합니다.
async function func() {
throw new Error('에러 발생!');
}
실제 상황에선 프로미스가 거부되기 전에 약간의 시간이 지체되는 경우가 있습니다. 이런 경우엔 await
이 에러를 던지기 전에 지연이 발생합니다.
await
이 던징 에러는 throw
가 던진 에러를 잡을 때처럼 try...catch
를 사용해 잡을 수 있습니다.
async function func() {
try {
let response = await fetch('http://유효하지-않은-주소');
} catch(err) {
alert(err);
}
}
func();
에러가 발생하면 제어 흐름이 catch
블록으로 넘어갑니다. 여러 줄의 코드를 try
로 감싸는 것도 가능합니다.
asycn function func() {
try {
let response = await fetch('http://유효하지-않은-주소');
let user = await response.json();
} catch(err) {
// fetch와 response.json에서 발행한 에러 모두를 여기서 잡습니다.
alert(err);
}
}
try..catch
가 없으면 아래 예시의 async 함수 func()
를 호출해 만든 프로미스가 거부 상태가 됩니다. func()
에 .catch
를 추가하면 거부된 프로미스를 처리할 수 있습니다.
async function func() {
let response = await fetch('http://유효하지-않은-주소');
}
// func()는 거부 상태의 프로미스가 됩니다.
func().catch(alert); // TypeError: failed to fetch // (*)
.catch
를 추가하는 걸 잊으면 처리되지 않은 프로미스 에러가 발생합니다(콘솔에서 직접 확인해 봅시다).
function 앞에 async
키워드를 추가하면 두 가지 효과가 있습니다.
await
를 사용할 수 있습니다.프로미스 앞에 await
키워드를 붙이면 자바스크립트는 프로미스가 처리될 때까지 대기합니다. 처리가 완료되면 조건에 따라 아래와 같은 동작이 이어집니다.
throw error
를 호출한 것과 동일함)async/await
를 함께 사용하면 읽고, 쓰기 쉬운 비동기 코드를 작성할 수 있습니다.
async/await
를 사용하면 promise.then/catch
가 거의 필요 없습니다. 하지만 가끔 가장 바깥 스코프에서 비동기 처리가 필요할 때같이 promise.then/catch
를 써야만 하는 경우가 생기기 때문에 async/await
가 프로미스를 기반으로 한다는 사실을 기억해야 합니다. 여러 작업이 있고, 이 작업들이 모두 완료될 때까지 기다리려면 Promise.all
을 활용할 수 있다는 점도 알고 계시기 바랍니다.