🫠
【TypeScript】非同期処理についてまとめる。Promise<T>,async/await編
はじめに
TypeScript で非同期処理を扱うについて、自分の知識が浅い部分があったので改めてまとめて行こうと思います。(初心者のため優しい目で見てください)
そもそも非同期処理って?
非同期処理とは、あるタスクの完了を待たずに、次のタスクを開始する処理のことです。
これに対し、同期処理は、一つのタスクが完了するまで次のタスクに進まず、順番に実行されます。
Promise<T>について
Promise
は「非同期処理の結果を表すオブジェクト」です。T
は解決時(成功時)に返される値の型を表します。
Promise は次の 3 状態を持ちます。
- pending: 処理中
- fulfilled: 成功(resolve が呼ばれた)
- rejected: 失敗(reject が呼ばれた)
Promise は pending → fulfilled または rejected のどちらかに遷移します。
型のバリエーション(例)
-
Promise<void>
→ 戻り値がない非同期処理
async function logMessage(): Promise<void> {
console.log("Hello, world!");
}
logMessage().then(() => {
console.log("処理完了"); // 処理完了
});
-
Promise<T | null>
→ 値がない可能性がある
function findUser(id: number): Promise<string | null> {
return new Promise((resolve) => {
if (id === 1) resolve("Alice");
else resolve(null);
});
}
// then で結果を受け取る
findUser(2).then((user) => {
if (user) {
console.log("ユーザー:", user);
} else {
console.log("ユーザーが見つかりません");
}
});
-
Promise<ResultType>
→ APIレスポンスや計算結果など
type User = { id: number; name: string };
// Promise<ResultType> の例
function fetchUser(): Promise<User> {
// 本来は fetch などで API から取得する想定
return new Promise((resolve) => {
resolve({ id: 1, name: "Alice" });
});
}
// then で結果を受け取る
fetchUser().then((user) => {
console.log(`ID: ${user.id}, 名前: ${user.name}`);
});
複数の非同期処理を順番に実行する方法
function step1(): Promise<number> {
return new Promise((resolve) => {
setTimeout(() => {
console.log("step1");
resolve(1);
}, 500);
});
}
function step2(input: number): Promise<number> {
return new Promise((resolve) => {
setTimeout(() => {
console.log("step2");
resolve(input + 1);
}, 500);
});
}
function step3(input: number): Promise<number> {
return new Promise((resolve) => {
setTimeout(() => {
console.log("step3");
resolve(input + 1);
}, 500);
});
}
// then を使ったチェーン
step1()
.then((r1) => step2(r1))
.then((r2) => step3(r2))
.then((r3) => console.log("最終結果:", r3))
.catch((err) => console.error("エラー:", err));
ポイント
- ネストが浅くなり可読性が向上
- エラー処理が
.catch
で一元化できる
Promise
を扱うユーティリティ
複数の-
Promise.all
すべての Promise が成功したら結果を配列で返し、1つでも失敗(reject)すると catch に飛ぶ
const p1 = Promise.resolve(1);
const p2 = Promise.resolve(2);
const p3 = Promise.resolve(3);
async function runAll() {
try {
const results = await Promise.all([p1, p2, p3]);
console.log("Promise.all 結果:", results); // [1, 2, 3]
} catch (err) {
console.error("Promise.all エラー:", err);
}
}
runAll();
-
Promise.allSettled
すべての結果(成功/失敗)を配列で返して、個別に成功・失敗を判定できる
const p4 = Promise.resolve(1);
const p5 = Promise.reject("失敗");
async function runAllSettled() {
const results = await Promise.allSettled([p4, p5]);
results.forEach((res) => {
if (res.status === "fulfilled") {
console.log("成功:", res.value);
} else {
console.log("失敗:", res.reason);
}
});
}
runAllSettled();
// 出力:
// 成功: 1
// 失敗: 失敗
-
Promise.race
最初に「settled(fulfilled / rejected)」したものを返す。つまり一番早い失敗も拾ってしまう
const p6 = new Promise((resolve) => setTimeout(() => resolve("遅い"), 1000));
const p7 = new Promise((resolve) => setTimeout(() => resolve("早い"), 500));
async function runRace() {
const result = await Promise.race([p6, p7]);
console.log("Promise.race 結果:", result); // "早い"
}
runRace();
-
Promise.any
最初に成功(fulfilled)した Promise を返して、すべて失敗すると AggregateError が発生
const p8 = Promise.reject("失敗1");
const p9 = new Promise((resolve) => setTimeout(() => resolve("成功"), 500));
const p10 = Promise.reject("失敗2");
async function runAny() {
try {
const result = await Promise.any([p8, p9, p10]);
console.log("Promise.any 結果:", result); // "成功"
} catch (err) {
console.error("Promise.any エラー:", err);
}
}
runAny();
まとめ
ユーティリティ | 動作 |
---|---|
Promise.all |
すべて成功したら配列で返す、1つでも失敗すると catch に飛ぶ |
Promise.allSettled |
成功・失敗すべての結果を配列で返す |
Promise.race |
最初に解決または拒否された結果を返す |
Promise.any |
最初に成功した結果を返す、すべて失敗すると AggregateError |
asyncについて
- async は 非同期関数を定義するキーワード
- async function は 必ず Promise<T> を返す関数 になる
- 関数内で return した値は自動で Promise.resolve(value) に変換される
awaitについて
- await は Promise が解決されるまで処理を一時停止して待機するキーワード
- async 関数内でしか使えない
- Promise が解決されると、その値を直接取得できる
エラーハンドリング
-
.catch
を使う
const promise: Promise<number> = new Promise((resolve, reject) => {
const success = false; // 成功か失敗かを切り替え
if (success) {
resolve(42);
} else {
reject("エラーが発生しました");
}
});
promise
.then((value) => {
console.log("成功:", value);
})
.catch((error) => {
console.error("捕捉されたエラー:", error);
})
.finally(() => {
console.log("処理終了");
});
-
try...catch
とasync/await
async function run() {
try {
const result: number = await new Promise<number>((resolve, reject) => {
const success = false;
if (success) {
resolve(100);
} else {
reject("非同期処理でエラー発生");
}
});
console.log("成功:", result);
} catch (error) {
console.error("捕捉されたエラー:", error);
} finally {
console.log("処理終了");
}
}
run();
非同期イテレーション
- 非同期で順番に値を取得する仕組み
- 通常の for...of は同期的に値を取得するが、
非同期イテレーションでは for await...of を使って Promise を順に解決しながら処理できる
async function* asyncGenerator() {
yield await Promise.resolve(1);
yield await Promise.resolve(2);
yield await Promise.resolve(3);
}
async function run() {
for await (const value of asyncGenerator()) {
console.log(value);
}
}
run();
// 出力:
// 1
// 2
// 3
ポイント
-
async function*
→ 非同期ジェネレーターを定義 -
for await...of
→ 非同期ジェネレーターの値を順番に取得
まとめ
-
非同期処理とは
タスクの完了を待たずに次の処理を開始できる仕組み。 -
Promise<T> の基本
Promise は非同期処理の結果を表すオブジェクト
3つの状態: pending / fulfilled / rejected -
型のバリエーション:
-
Promise<void>
→ 戻り値なし -
Promise<T | null>
→ 値がない可能性あり -
Promise<ResultType>
→ API などの結果
-
-
非同期処理の連鎖と複数処理の扱い方
.then
でチェーンして順番に実行可能
Promise.all
/Promise.allSettled
/Promise.race
/Promise.any
で複数 Promise を扱える -
async / await
-
async
→ 非同期関数を定義、常に Promise を返す -
await
→ Promise が解決されるまで待機して値を取得
コードが同期処理のように書けて可読性が向上
-
-
エラーハンドリング
.catch
で Promise のエラーを捕捉
try/catch
+async/await
で同期的に処理しても捕捉可能 -
非同期イテレーション
-
async function*
で非同期ジェネレーターを作成 -
for await...of
で Promise を順に解決しながら処理
-
Discussion