async/await 문법과 try/catch 구문을 섞어 쓰면서 했던 실수를 기록한다.
에러가 발생할 수 있는 비동기 함수가 있고, 그 함수에서 발생하는 에러를 처리하고 싶었다. 그래서 다음과 같은 코드를 작성했다.
async function getValue() {
try {
return myAsyncFunction();
} catch (error) {
console.error(error);
return undefined;
}
}
이렇게 하면 myAsyncFunction
실행중 발생한 에러가 잡혀서 getValue
함수는 undefined를 반환할 것이라고 생각했다. 하지만 getValue
함수 바깥으로 계속 에러가 던져지고 있었다.
코드를 좀 더 유심히 바라보다 원인을 알게 되었는데, 함수의 실행 결과를 바로 리턴하는 것이 문제였다. myAsyncFunction
은 비동기 함수이니까 Promise를 반환할 테고, getValue
도 같은 Promise 객체를 반환할 뿐이다. Promise의 최종 계산은 getValue
를 await하고 있는 지점에서 이뤄지니까 에러는 잡히지 않는다.
이를 원하는 대로 작동하게 만들려면 getValue
에서 myAsyncFunction
의 Promise를 resolve 해줘야 한다.
async function getValue() {
try {
const value = await myAsyncFunction();
return value;
} catch (error) {
console.error(error);
return undefined;
}
}
Promise의 "await"을 위한 변수 선언이 눈에 띈다. 왜냐면 return await
은 린트 규칙으로 막고 있었거든.
(2021년 10월 13일 업데이트)
no-return-await
규칙이 return await
를 무조건 막는 줄 알았는데 아니었다. try/catch 안의 return await
은 허용하고 있었다. 그냥 내가 이렇게 코딩해야하는 것을 모르고 있었을 뿐...
따라서 다음과 같은 형태를 쓸 수 있다.
async function getValue() {
try {
return await myAsyncFunction();
} catch (error) {
console.error(error);
return undefined;
}
}
에러는 항상 발생하는 것이 아니라 잘못 코딩한 것을 지나치기 쉽다. 테스트 코드를 잘 짜던지, 아니면 더글라스 크락포드 형님이 말씀하셨듯 정말 예상치 못한 상황에서만 에러를 발생하게 함수를 설계해야겠다.
아예 async/await 문법을 쓰지 말아볼까...?
myAsyncFunction({
onSuccess: (value) => {
handleValue(value)
},
onFail: (error) => {
console.error(error)
handleValue(undefined)
},
})
결과를 기다리기 위한 변수 선언이 없고, 모든 걸 함수로 표현한다는 장점이 느껴진다. 하지만 콜백 헬이 두렵기도 하고... 아직 async/await이 편해보인다. 🤔