
Promise의 메서드 체이닝에 의한 코드 가독성의 문제점을 해결할 방법으로 배웠던 async, await의 적절한 사용법에 대해서 알아보자
기존에 비동기로직 문제를 해결하기 위해서 Callback Function 더 나아가 Promise을 이용했었다.
하지만 위 두 방법에는 코드구현상에 분명한 한계가 있었고 이를 위해 새로운 방법이 2017년에 ES8에서 나온 문법이 있다.
async, await은 JavaScript에서 비동기로직을 동기로직으로 처리할 수 있게해주는 문법이다.
먼저 앞선 2가지 방법들을 간단하게 살펴보고 문제점이 뭐였는지 알아보자
setTimeout(() => {
console.log('Step 1');
setTimeout(() => {
console.log('Step 2');
setTimeout(() => {
console.log('Step 3');
// 여기에 더 많은 중첩된 작업들이 올 수 있다
setTimeout(() => {
console.log('Final step');
}, 1000);
}, 1000);
}, 1000);
}, 1000);
위 코드가 대표적인 Callback Function을 이용한 비동기로직 처리이다.
한눈에 봐도 알듯 어떠한 함수 안에 연쇄적으로 로직을 처리하다보니 코드가 굉장히 복잡하다.
코드가 복잡하다는건 가독성이 심각하게 안좋다는 것인데 이러한 코드는 해당 로직안에서 에러가 발생하여 해당 에러를 찾든 팀프로젝트에서 다른사람과 코드리뷰를 통해서 해당 코드를 리뷰한다고 하면 굉장히 진땀 뺄 일이 일어날게 뻔하다.
function delay(time) {
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, time);
});
}
delay(1000)
.then(() => {
console.log('Step 1');
return delay(1000);
})
.then(() => {
console.log('Step 2');
return delay(1000);
})
.then(() => {
console.log('Step 3');
// 여기에 더 많은 중첩된 작업들이 올 수 있음
return delay(1000);
})
.then(() => {
console.log('Final step');
});
.catch(() => {
console.log("Error!!!!");
});
위 로직은 비동기 앞선 코드보다는 그래도 코드를 읽음에 있어서 훨씬 괜찮아 지긴 했지만 만약 여기서 "Promise"로 처리해야 하는 데이터가 단순한 한 두단계가 아닌 여러단계를 거쳐야한다면 어떨까?
아마도 .then메서드가 길게 체이닝된 로직일 것이다.
Promise메서드 체이닝이 길게 일어난 로직은 Callback Function 지옥에서 벗어난 우리에게 또 다른 Promise Hell을 몸소 느끼게 해줄 것이다.
그렇다면 이러한 비동기 로직을 동기처럼 보이게 해주는 문법은 뭘까?
바로 이 포스팅에 핵심인 async, await 문법이다.
function delay(time) {
return new Promise((resolve) => {
setTimeout(() => {
resolve();
}, time);
});
}
async function runSteps() {
try {
await delay(1000);
console.log('Step 1');
await delay(1000);
console.log('Step 2');
await delay(1000);
console.log('Step 3');
// 여기에 더 많은 중첩된 작업들이 올 수 있음
await delay(1000);
console.log('Final step');
} catch (error) {
console.error('Error:', error);
}
}
runSteps();
위 예시코드를 보면 앞선 코드보다 가독성이 더 좋다고 느껴질 것이다.
물론 처음에는 "뭐 크게 다른게 없는데..?"라고 생각했는데 Promise를 이용해서 다시 짜려고 하면 가독성에서 역체감이 아주 심했다..
async function runSteps() {
try {
await delay(1000); //비동기 처리
console.log('Step 1');
await delay(1000); //비동기 처리
console.log('Step 2');
await delay(1000); //비동기 처리
console.log('Step 3');
// 여기에 더 많은 중첩된 작업들이 올 수 있음
await delay(1000); //비동기 처리
console.log('Final step');
} catch (error) {
console.error('Error:', error);
}
}
await문법은 비동기로 동작하는 함수 앞에 해당 함수가 실행되기도 마무리되기를 기다려라 하는 뜻으로 'await'을 사용할 수 있다.
즉, await이 앞에 있는 로직은 해당 로직이 마무리되기 전까지 다음 로직이 실행되지 않는다.
비동기로 동작하는 코드를 동기적으로 바꾼 코드인 것이다.
하지만 여기서 await은 하나의 제약사항이 있다.
await은 반드시 특정 함수 내부에서 실행되어야 한다.
그럼 특정함수는 무엇일까?
바로 async함수에서만 await을 사용할 수 있다.
async function runSteps() {
try {
await delay(1000); //비동기 처리
console.log('Step 1');
........
await delay(1000); //비동기 처리
console.log('Final step');
} catch (error) {
console.error('Error:', error);
}
}
async문법은 JavaScript에서 비동기로직을 처리하는 방법 중 하나이다.
async키워드를 함수 앞에 붙여주면 해당 함수는 Promise를 반환하는 함수가 되어서 해당 함수 내부에서는 await키워드를 사용해서 동기적으로 로직을 짤 수 있는 것이다.
await키워드를 사용하기 위해선 async이 앞에 붙어있는 함수 내부에서 사용해야하므로 async는 await을 통한 비동기 처리에 필수적인 문법인 것이다.
try {
await delay(1000); //비동기 처리
console.log('Step 1');
........
await delay(1000); //비동기 처리
console.log('Final step');
} catch (error) {
console.error('Error:', error);
}
Promise에서는 에러처리를 연쇄된 .then뒤에 catch문을 이용했다면 async, await문법에서는 try ~ catch문을 이용해서 에러 핸들링을 한다.
Promise, Callback Fucntion외에 비동기 처리 문법인 async, await에 대해서 알아보았다.
다만 해당 문법에 대해서 자세히 알아보니 만능같아 보였던 async, await방법에도 문제점이 하나 존재했다.
지나치게 연속적인 await문법은 병목현상을 일으켜서 3초면 해결될 비동기 처리 로직이 해당 시간보다 오래 걸리는 문제가 있었다.
해당 문제에 대해서도 어떻게 하면 문제가 발생하고 적절한 해결법이 무엇인지 알아봐야할거 같다