TypeScript Promise 詳解
Promise は JavaScript の非同期プログラミングの基礎であり、TypeScript は Promise を完全に型サポートしています。
ジェネリック型パラメータを使用することで、Promise の解決値と拒否値の型を正確に指定できます。
SVG 図:Promise のワークフロー
なぜ Promise が必要か
JavaScript では、ネットワークリクエスト、ファイル読み取り、タイマーなど、多くの操作が非同期です。
Promise は統一された非同期プログラミングインターフェースを提供し、非同期コードをより簡単に記述・管理できるようにします。
TypeScript はジェネリックのサポートにより、Promise の型安全性を保証します。
概念の説明:Promise は非同期操作の最終結果を表すオブジェクトです。状態は 3 つあります:pending(実行中)、fulfilled(成功)、rejected(失敗)。
Promise の作成
Promise コンストラクタを使用して Promise を作成し、実行関数(executor)を渡します。
実例
// Promise<string> は成功時に文字列を返すことを示す
var promise = new Promise<string>(function(resolve, reject) {
var success = true;
if (success) {
// resolve を呼び出すと操作成功を表し、結果値を渡す
resolve("成功!");
} else {
// reject を呼び出すと操作失敗を表し、エラーを渡す
reject(new Error("失敗"));
}
});
// then を使用して成功時の処理を行う
promise.then(function(value) {
console.log("完了: " + value);
})["catch"](function(error) {
// catch を使用して失敗時の処理を行う
console.log("エラー: " + error.message);
});
実行結果:
完了: 成功!
ジェネリックの説明:
Promise<T>`Promise<T>` の T は Promise が成功したときの値の型です。これにより TypeScript は戻り値の型を推論できます。
Promise のチェーン呼び出し
then と catch メソッドは新しい Promise を返すため、チェーン呼び出しができます。
実例
var promise = Promise.resolve(1)
.then(function(n) {
// 最初の then、n = 1
return n * 2; // 2 を返す
})
.then(function(n) {
// 2 番目の then、n = 2
return n + 10; // 12 を返す
})
.then(function(n) {
// 3 番目の then、n = 12
console.log("最終結果: " + n);
return n;
});
console.log("Promise チェーン: " + promise);
実行結果:
最终结果: 12 Promise 链: [object Promise]
チェーン呼び出し:各 then は新しい Promise を返すため、複数の非同期操作を順番に実行できます。
Promise.all
Promise.all はすべての Promise の完了を待ち、すべての結果を含む配列を返します。
実例
var p1 = Promise.resolve(1);
var p2 = Promise.resolve(2);
var p3 = Promise.resolve(3);
// Promise.all はすべての Promise の完了を待つ
// すべての Promise の結果を含む配列を返す
Promise.all([p1, p2, p3]).then(function(results) {
console.log("すべて完了: " + results);
// 合計を計算
console.log("合計: " + results.reduce(function(a, b) { return a + b; }, 0));
});
実行結果:
全部完成: 1,2,3 总和: 6
注意:いずれかの Promise が失敗した場合、Promise.all は直ちに rejection となり、他の Promise の完了を待ちません。
Promise.race
Promise.race は最初に完了した(成功または失敗を問わない)Promise の結果を返します。
実例
var p1 = new Promise(function(resolve) {
setTimeout(function() { resolve("p1"); }, 100);
});
var p2 = new Promise(function(resolve) {
setTimeout(function() { resolve("p2"); }, 50);
});
var p3 = new Promise(function(resolve) {
setTimeout(function() { resolve("p3"); }, 30);
});
// Promise.race は最初に完了した Promise の結果を返す
Promise.race([p1, p2, p3]).then(function(value) {
console.log("最初に完了: " + value);
});
実行結果:
最先完成: p3
適用例:Promise.race はタイムアウト機能の実装によく使われます。長時間実行される Promise とタイムアウト用 Promise を競わせます。
Promise.allSettled
Promise.allSettled はすべての Promise の終了を待ち(成功・失敗を問わず)、各 Promise のステータスと結果を返します。
実例
var p1 = Promise.resolve("成功");
var p2 = Promise.reject(new Error("失敗"));
var p3 = Promise.resolve("完了");
// Promise.allSettled はすべての Promise の終了を待つ
// 各 Promise のステータスと値/reason を返す
Promise.allSettled([p1, p2, p3]).then(function(results) {
results.forEach(function(result, index) {
if (result.status === "fulfilled") {
console.log("Promise " + index + ": " + result.value);
} else {
console.log("Promise " + index + ": " + result.reason.message);
}
});
});
実行結果:
Promise 0: 成功 Promise 1: 失败 Promise 2: 完成
違い:Promise.all は最初の失敗時にすぐに停止します。Promise.allSettled はすべての Promise の終了を待ちます。
Promise の型注釈
TypeScript のジェネリックサポートにより、Promise の型宣言が正確になります。
実例
// Promise<{ name: string; age: number }> は返されるユーザーオブジェクトの型を指定している
function getUser(): Promise<{ name: string; age: number }> {
return Promise.resolve({ name: "Alice", age: 25 });
}
// async 関数:暗黙的に Promise を返す
async function main() {
// await は user の型を自動的に推論する
var user = await getUser();
console.log("ユーザー: " + JSON.stringify(user));
}
main();
型推論:TypeScript はジェネリックパラメータに基づいて Promise の戻り値の型を自動的に推論するため、async/await でも完全な型ヒントを得られます。
注意事項
- ジェネリックパラメータ:常に Promise にジェネリックパラメータを指定し、戻り値の型を明確にします
- エラー処理:Promise が失敗した場合に備えて catch を使用することを忘れないでください
- all vs allSettled:すべての結果が必要な場合は allSettled を、迅速に失敗させたい場合は all を使用します
- async/await:現代のコードでは async/await の使用が推奨されます。構文がより簡潔です
ベストプラクティス:async/await 構文を優先的に使用してください。本質的には Promise に基づいていますが、同期コードのように記述できます。
まとめ
Promise は TypeScript の非同期プログラミングの中核です。
- Promise:非同期操作のコンテナであり、pending/fulfilled/rejected の 3 つの状態があります
- then/catch:非同期結果をチェーンで処理
- Promise.all:すべての完了を待ち、いずれかが失敗すると全体が失敗する
- Promise.race:最初に完了した結果を返す
- Promise.allSettled:すべての終了を待ち、各状態を返す
x その他の拡張推奨:async/await 構文を Promise と組み合わせて使用し、非同期コードを型安全かつ読みやすくします。