비동기 코드를 동기식으로 표현해서 간단하게 표현하기 위해 사용하는 async / await는 가장 최근에 나온 문법이다. 기존의 비동기 처리 방식인 콜백 함수와 프러미스의 단점을 보완하고 개발자가 읽기 좋은 코드를 작성할 수 있게 도와준다.
async function 함수명() {
await 비동기_처리_메서드_명();
}
먼저 함수의 앞에 async
라는 예약어를 붙인다. 그리고 나서 함수의 내부 로직 중 HTTP 통신을 하는 비동기 처리 코드 앞에 await
를 붙인다.
일반적으로 await
의 대상이 되는 비동기 처리 코드는 프로미스를 반환하는 API 호출 함수이다.(반드시 await
가 의도한 대로 동작해야한다)
function fetchItems() {
return new Promise(function(resolve, reject) {
var items = [1,2,3];
resolve(items)
});
}
async function logItems() {
var resultItems = await fetchItems();
console.log(resultItems); // [1,2,3]
}
fetchItems()
함수는 프러미스 객체를 반환하는 함수이다.
fetchItems()
함수를 실행하면 프러미스가 resolved되며 결과 값은
items
배열이 된다.
logItmes()
함수를 실행하면 fetchItems()
함수의 결과값인 items
배열이 resultItems
변수에 담긴다. 콘솔에는 [1, 2, 3]이 출력된다.
await를 사용하지 않았다면 데이터를 받아온 시점에 콘솔을 출력할 수 있도록 콜백함수나, .then() 등을 사용해야 했을 것이다.
async function findUser() {
try {
let user = await Users.findOne({}).exec();
user.name = 'hodoo';
user = await user.save();
user = await Ueser.findOne({gender: 'm'}).exec();
...
} catch (err) {
console.error(err);
}
}
await는 promise를 받아 처리하는 키워드입니다. async함수는 promise가 없으면 의미가 없습니다. 그리고 await 키워드를 사용하려면 함수가 async 함수로 선언되야 한다. async 함수는 화살표 함수로도 가능하고, 함수 표현식으로도 가능하다.
const functionExpression = async function() {
console.log('함수 표현식');
};
const arrowFunction = async () => {
console.log('화살표 함수');
};
const IIFE = (async () => {
console.log('즉시 실행 함수 표현식');
})();
주의할 점은 await는 반드시 async함수 바로 안에서만 쓰여야 한다는 점이다.
async function a() {
//(function b() {
// await Promise.resolve(true); async 함수 바로 안이 아니라서 에러. 아래와 같이 수정
(async function b() {
await Promise.resolve(true); // 정상 작동되도록 함수 내부에 async 위치
})();
}
const returnPromise = async() => {
return 'hodoo'
};
returnPromise().then((res) => {
console.log(res); // 'hodoo'
});
async 함수는 return 또는 throw 값이 담긴 Promise를 리턴합니다.
promise를 리턴하기 때문에 코드의 then(성공)
또는 catch(실패)
여부에 따라 다시 await로 연결할 수 있다.
async fuction another() {
try {
let result = await returnPromise();
} catch (err) {
console.error(err);
}
}
async 함수 안에 여러 await가 있을때 앞의 await가 완료된 후에야 뒤의 await가 실행된다. 동시에 실행하려면 await Promise.all([프로미스들])
을 작성해야 한다.
promise가 도입되었음에도 콜백을 사용하는 것처럼, async/await 모두 프로미스나 콜백의 완벽한 대체품이 아니다. 경우에 따라 사용할 수 있는 것이다.
const promise = new Promise(function (resolve, reject) {
// do something async here..
fs.readFile(path, "utf-8", (err, data) => {
if (err) {
reject(err);
} else {
resolve(data);
}
});
});
promise.then(functuon done (date) {
console.log("promise success!", date);
return 1;
}).then(function handleOne (one) {
console.log("I am one");
return one + 1;
}).then(function handleTwo (two) {
console.log("i am two");
}).catch(function handleError (err) {
console.log("promise Failed!", err);
});
promise
내부 비동기 로직, done
함수, handleOne
함수, 혹은 handleTwo
함수 어디서 에러가 나더라도 handleError
함수가 호출된다. 기존 try..catch
와 유사한 이 흐름은 자바스크립트 개발자들이 친숙한 흐름의 에러 핸들링일 뿐 아니라 실패 로직과 성공 로직의 분리가 더욱 명확해질 수 있게 돕는다.
async & await에서 예외를 처리하는 방법은 바로 try Catch이다. 프러미스에서 에러처리를 위해 .catch()
를 사용했던 것처럼 async에서는 catch {}
를 사용하면 된다.
async function logToDoTitile() {
try {
let user = await fetchUser();
if (user.id === 1) {
let todo = await.fetchTodo();
console.log(todo.title);
}
} catch (error) {
console.log(error);
}
}
위 예시 코드는 네트워크 통신 오류뿐만 아니라 간단한 타입 오류 등의 일반적인 오류까지도 catch로 잡아낼 수 있다. 발견된 에러는 error
객체에 담기기 때문에 에러의 유형에 맞게 에러 코드를 처리해주면 된다.