
지난 파트에서 Promise에 대해 살펴보았다. Promise는 Callback Hell을 깔끔하게 해결해주는 고마운 녀석이었지만, 얘도 chaining을 계속하다 보면 코드가 조금 난잡해질 수 있다.
이때 async와 await이라는 특별한 문법을 사용하면 Promise를 조금 더 깔끔하게 사용할 수 있다.
마치 동기식으로 코드를 작성하는 것처럼 간편하게 코드를 작성할 수 있도록 도와준다.
요약 : Promise를 깔끔하게 사용하게 해준다!
async와 await에 대해 알아보기 전에 비동기 처리의 필요성과 Promise의 간단한 사용법을 되짚어보자.
코드 출처 : 드림코딩 by 엘리 유튜브
function fetchUser() {
// 10초 정도 걸리는 서버에서 사용자 데이터를 받아오는 코드가 있다고 가정.
return 'complete'
}
const user = fetchUser();
console.log("user 데이터 가져오기 완료");
console.log("Page UI 표시!");
javascirpt 엔진은 기본적으로 순서대로 위에서부터 아래로 한 줄씩 코드를 실행시킨다. 이는 바로 동기적인 처리를 하고 있는 것이다.
위 코드에서 그 어떤 비동기 처리도 하지 않으면 10초가 지난 뒤에야 console에 "page UI 표시!"가 출력될 것이다.
즉 실제 어떤 서비스를 제공하는 웹페이지라고 치면, 데이터를 받아오는 10초 동안 사용자는 그 어떤 UI요소도 렌더링 되지 않은 텅빈 지루한 화면을 보고 있을 것이다.
이게 비동기처리의 필요성이었다.
function fetchUser() {
return new Promise((resolve, reject) => {
// 10초 정도 걸리는 서버에서 사용자 데이터를 받아오는 코드가 있다고 가정.
resolve('complete')
})
}
const user = fetchUser();
user.then(console.log("user 데이터 가져오기 완료"))
console.log("Page UI 표시")
우리는 이런 비동기 처리를 위해 지난번에 위와 같이 Promise api를 사용했었다.
async는 위에서 말했던 대로 위와 같은 Promise를 조금 더 깔끔하게 쓸 수 있게 도와주는 syntactic sugar이다. 사용법은 놀랍게도 간단하다.
async function fetchUser() {
// 10초 정도 걸리는 서버에서 사용자 데이터를 받아오는 코드가 있다고 가정.
return 'complete'
}
const user = fetchUser();
user.then(console.log("user 데이터 가져오기 완료"))
console.log("Page UI 표시")
위에서 봤던 예제와 동일한 코드이다. 대충봐도 Promise보다 훨씬 더 간결해진걸 알 수 있다.
이는 function 앞에 async라는 키워드를 붙여주는 것만으로도 가능하다.
저렇게 async를 붙여주면 해당함수 코드블록은 Promise를 반환한다!
await은 async 키워드가 붙은 함수내에서만 동작하는 키워드다.
(일반함수에서 사용하게 되면 Syntax error가 발생한다.)
기본적으로 await을 만나면 Promise가 처리(settled)될 때까지 함수 실행을 기다린다.
Promise가 처리되면 그 결과와 함께 실행이 재개된다. 처리되는 동안에는 비동기적으로 엔진이 다른 일을 할 수 있게 만들어준다.
function delay(ms) {
return new Promise(resolve => setTimeout(resolve,ms));
}
async function test() {
await delay(3000);
console.log('완료');
}
test();
위 코드를 보자.
단지 delay라는 Promise 앞에 await를 붙여주는 것 만으로도 3초를 기다렸다가 완료라는 문자열을 콘솔에 출력하는 Promise가 만들어졌다.
Promise에서 then의 역할을 대신한다고 생각하면 편할 것 같다.
아래는 Promise의 then을 사용한 코드이다.
function delay(ms) {
return new Promise(resolve => setTimeout(resolve,ms));
}
function test() {
return delay(3000)
.then(console.log('완료'))
}
test();
사실 코드가 짧아서 잘 느껴지지 않을 수도 있지만 await가 조금 더 직관적인 느낌이고,
긴 코드를 짠다고 가정했을 때 Promise를 사용한다면 코드가 꽤나 복잡해질 것이다.
Promise에서는 .catch를 체이닝하는 것으로 에러를 잡을 수 있었다.
async await에서는 javascript가 제공하는 try..catch문을 사용할 수 있다.
try {
// 코드...
} catch (err) {
// 에러 핸들링
}
try..catch는 다음과 같이 동작한다.
1. 먼저, try {...} 안의 코드가 실행되고,
2. 에러가 없다면 try 블록 안의 마지막 줄까지 실행되고 catch 블록은 건너 뛴다.
3. 에러가 있다면 try 안 코드의 실행이 중단되고 catch(err)블록으로 넘어간다. 변수 err에는 어떤 에러인지에 대한 설명이 담긴 객체가 포함된다.
따라서 이 형태에 맞게 try 코드를 감싸고, 나머지 에러를 적절하게 핸들링하면 된다.
function delay(ms) {
return new Promise(resolve => setTimeout(resolve,ms));
}
async function test() {
try {
await delay(3000);
console.log('완료');
}
catch(err) {
console.log(err);
}
}
test();