TypeScript Promise 詳解

Promise は JavaScript の非同期プログラミングの基礎であり、TypeScript は Promise を完全に型サポートしています。

ジェネリック型パラメータを使用することで、Promise の解決値と拒否値の型を正確に指定できます。


SVG 図:Promise のワークフロー 背景 タイトル Promise のワークフロー Promise の作成 Promise の作成 new Promise((resolve, reject) => {...}) 矢印 pending 待機中 分岐:resolve/reject fulfilled ✓ rejected ✗ 下半分:メソッド then() · catch() · finally() 矢印マーク

なぜ Promise が必要か

JavaScript では、ネットワークリクエスト、ファイル読み取り、タイマーなど、多くの操作が非同期です。

Promise は統一された非同期プログラミングインターフェースを提供し、非同期コードをより簡単に記述・管理できるようにします。

TypeScript はジェネリックのサポートにより、Promise の型安全性を保証します。

概念の説明:Promise は非同期操作の最終結果を表すオブジェクトです。状態は 3 つあります:pending(実行中)、fulfilled(成功)、rejected(失敗)。


Promise の作成

Promise コンストラクタを使用して Promise を作成し、実行関数(executor)を渡します。

実例

// Promise を作成し、ジェネリックで解決値の型を指定
// 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 を返すため、チェーン呼び出しができます。

実例

// チェーン呼び出し:各 then は新しい値を返し、次の then が受け取る
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 の完了を待ち、すべての結果を含む配列を返します。

実例

// 3 つの 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 の結果を返します。

実例

// 遅延時間の異なる 3 つの 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 のステータスと結果を返します。

実例

// 3 つの Promise を作成。そのうち 1 つは失敗する
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 を返す関数を定義
// 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:すべての終了を待ち、各状態を返す

推奨:async/await 構文を Promise と組み合わせて使用し、非同期コードを型安全かつ読みやすくします。

x その他の拡張