## [[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) ```