## [[Promise]]와 [[async · await]] 차이점
- 코드 가독성
- `Promise` : 체이닝으로 인한 들여쓰기와 콜백 구조 => 코드가 복잡함
- `async · await` : 동기 코드와 유사한 직관적인 구조 => 읽기 쉬움
- 에러 처리
- `Promise` : `.catch()` 메서드 체이닝 사용
- `async · await` : `try-catch` 블록 사용
## 장 · 단점
| 항목 | Promise | async · await |
| -------- | ----------------------------------------- | --------------------------- |
| 가독성 | 🟡 중첩이 길어지는 경우 가독성 떨어짐 | 🟢 동기 흐름과 비슷해서 직관적임 |
| 에러 처리 | 🟡 체이닝을 이용하므로 중간에 에러 누락 가능성 있음 | 🟢 try/catch로 일관된 에러 처리 가능 |
| 병렬 처리 | 🟢 Promise.all을 이용해서 여러 Promise를 병렬로 처리 가능 | 🟡 기본적으로 순차 실행 |
| 조건 분기 처리 | 🟡 then 내부에서 분기 처리하기 복잡함 | 🟢 if/else 같이 동기식으로 분기 처리 가능 |
| 제어 흐름 | 🟡 복잡한 제어 흐름 구현 어려움 | 🟢 동기식 반복문, 조건문을 사용하기 쉬움 |
## 둘을 같이 쓸 수 있을까?
async/await은 Promise를 기반으로 하는 문법이기 때문에 둘을 함께 사용할 수 있다.
```javascript
function fetchData(id) {
return id > 0 ? Promise.resolve('OK') : Promise.reject('Error');
}
async function run() {
const result = await fetchData(1) // 👈 Promise 체이닝과 async/await 혼합 사용
.then(data => data)
.catch(error => '에러 발생!: ' + error);
console.log(result);
}
run(); // OK 또는 에러 발생!: Error 출력
```
`.catch()` 메서드와 try-catch를 함께 사용할 때에는 `.catch()`에서 먼저 에러가 처리되고 try-catch로 에러가 전달되지 않는다는 점을 주의해야 한다.
```javascript
function fail() {
return Promise.reject('error!');
}
async function run() {
try {
await fail().catch(error => {
console.log('caught in .catch:', error);
});
} catch (error) {
console.log('caught in try/catch:', error);
}
}
run(); // caught in .catch: error!
```
이렇게 에러 흐름이 분산되면 디버깅이 어려워 질 수 있다는 단점이 있으므로 한 함수 안에서는 Promise 체이닝 또는 async/await 방식 중 하나로 일관된 스타일을 유지하는 것이 좋다.
## 사용 패턴
- 병렬 처리가 필요한 경우 => **Promise**
async/await을 사용하면 각 작업이 동기적으로 진행되면서 작업 개수에 비례하는 시간이 필요하게 된다. => [[Promise.all]] 이나 [[Promise.allSettled]] 를 활용한다.
- 복잡한 조건 분기나 에러 처리가 있는 경우 => **async/await**
체이닝을 사용하는 경우 코드 가독성이 나빠지거나 에러 처리 로직이 복잡해진다 => async/await과 try-catch 활용
- 순차적인 작업이 필요한 경우 => **Promise**
async/await를 사용하면 단계별 로직이 필요한 경우에 흐름이 쪼개지게 된다. 데이터 가공 등의 작업에서 **여러 함수들을 조합**해서 사용해야 할 때는 체이닝을 쓰는 것이 좀 더 가독성에 좋을 수 있다. (함수형 스타일?)
```javascript
function fetchUser() {
return fetch('/api/user').then(res => res.json());
}
function validateUser(user) {
if (!user.name) throw new Error('사용자가 존재하지 않습니다!')
return user;
}
function enrichUser(user) {
user.fullName = `${user.firstName} ${user.lastName}`;
return user;
}
function welcome(user) {
console.log(`환영합니다. ${user.fullName}님!`);
}
fetchUser()
.then(validateUser) // 사용자 유효성을 검증하고
.then(enrichUser) // fullName을 생성해서
.then(welcome) // 환영 메시지를 출력하기
.catch(error)
```