項目27 コールバックの代わりにasync関数を使用して型の流れを改善する
非同期処理の組み合わせやすさと、型の流れ改善のため、コールバックではなくPromiseを利用する。可能であればasync / awaitを利用し、非同期処理のPromise扱いを強制させる
Promiseを使ったほうが良い理由
Promise or async / awaitはコールバックよりも組み合わせやすい
複数非同期処理を行う場合、コールバックはネストが深くなる傾向にある
code:ts
fetchURL(url1, function(response1) {
fetchURL(url2, function(response2) {
fetchURL(url3, function(response3) {
// ...
console.log(1);
});
console.log(2);
});
console.log(3);
});
console.log(4);
Promise.allを使ってすべての非同期処理を一括りにできる
分割代入とawaitを組み合わせることで型推論を行える
code:ts
async function fetchPages() {
// response1,response2などはResponse型として推論される
const response1, response2, response3 = await Promise.all([
fetch(url1), fetch(url2), fetch(url3)
]);
}
Promise or async / awaitを使うコードのほうがコールバックを使うコードよりも型の流れが改善される
Promise.raceと型推論も相性が良い
Promise.raceは複数のPromiseの内最初の1つが解決したタイミングで、そのPromiseを返す
この仕組みを使って、Promiseにタイムアウトを追加できる
code:ts
function timeout(timeoutMs: number): Promise<never> {
return new Promise((resolve, reject) => {
setTimeout(() => reject('timeout'), timeoutMs);
});
}
async function fetchWithTimeout(url: string, timeoutMs: number) {
return Promise.race(fetch(url), timeout(timeoutMs));
}
fetchWithTimeoutの戻り値の型はPromise<Response>と推論される
本来はPromise<Response | never>だが、never(空集合)とユニオンをとっても集合の大きさは変わらないため省略されている
async関数が常にPromiseを返すことのメリット
混乱を引き起こすようなバグを避けれる点
戻り値の型が必ず、Promiseでラップされるため、関数を利用する側で非同期処理が行われることが一目でわかる
code:ts
const getNumber = async () => 42;
// const getNumber: () => Promise<number>
asyncを使わない場合は、Promiseを直接返すよう実装する必要が生まれる
code:ts
const getNumber = () => Promise.resolve(42);
// const getNumber: () => Promise<number>
asyncからPromiseをラップして返しても、戻り値の型はPromise で二重にラップされないことに注意
code:ts
async function getJSON(url: string) {
const response = await fetch(url);
const jsonPromise = response.json();
return jsonPromise; // const jsonPromise: Promise<any>
}
getJSON // function getJSON(url: string): Promise<any>
#TypeScript