이펙티브 타입스크립트 (댄 밴더캄 지음) 를 읽고 정리
📍요약
✅콜백함수보다 프로미스를 사용하는게 좋다 (코드 간결성뿐만 아니라 타입 추론에도 도움을 준다)
✅프로미스를 생성하는 것보다 async/await을 사용하는게 좋다
- 간결하고 직관적인 코드를 작성할 수 있고, 의도치 않은 에러를 제거할 수 있다
✅어떤 함수가 프로미스를 반환한다면 async로 선언해주는 것이 좋다
📍Promise.race를 사용하여 프로미스에 시간제한 추가하는 패턴
서버에 요청을 보내고 마냥 기다릴 수 없는 경우, Promise.race에 fetch와 타임아웃을 같이 넣는다
- 이를 통해 fetch에 시간 제한을 만들 수 있다
// 특정 시간 후에 reject되는 프로미스 반환
// 무조건 reject되므로 반환 타입이 never
// 원래 반환 타입은 Promise<Response>
function timeout(millis: number): Promise<never> {
return new Promise((resolve, reject) => {
setTimeout(() => reject('timeout'), millis);
});
}
// fetch가 의도하는 시간안에 성공하면 fetch의 프로미스 반환하고
// 시간안에 응답이 없으면 timeout의 reject되는 프로미스 반환
async function fetchWithTimeout(url: string, ms: number) {
// Promise.race(프로미스1, 프로미스2, ...) -> 빨리 resolve or reject 되는 것대로 실행
return Promise.race([fetch(url), timeout(ms)]);
}
위 코드의 타입을 살펴보면, fetchWithTimeout의 반환 타입이 Promise<Response>로 추론된다.
그 이유는...
- Promise.race의 반환 타입은 입력 타입들의 유니온 타입이다
- timeout 함수와 fetch를 고려하면, Promise<Response | never> 가 된다
- never (공집합) 와의 유니온은 아무런 효과가 없으므로, 결과가 Promise<Response> 로 간단해진다
📍async/await 을 사용해야 하는 이유
1️⃣Promise 체인이나 콜백 지옥보다 간결하고 직관적임
2️⃣항상 프로미스를 반환하도록 강제된다
- 예시
함수 내부에 Promise가 없어도 promise 반환
async function getNumber() {
return 42; // // Type is Promise<number>
}
// 화살표 함수 버전
const getNumber = async () => 42; // Type is () => Promise<number>
// 즉시 사용 가능한 값에도 프로미스를 직접 리턴하는 버전 (권장)
const getNumber = () => Promise.resolve(42); // Type is () => Promise<number>
함수는 항상 동기 또는 비동기로 실행되어야 하기 때문에 (혼용되면 안됨)
프로미스를 직접 리턴하는 함수 방식은 코드를 비동기 함수로 통일하는데 도움을 준다