Future/Promiseとは何か、非同期処理とプログラミング

著者: IT Sectr 公開日: 2026-03-17 読了時間: 9 分

Future/Promiseは、未完了の処理の結果を保持するための非同期プログラミングのパターンです。dart.devの非同期プログラミングに関するドキュメントによると、Futureは遅延実行された計算の読み取り専用の結果であり、Promiseはその結果を埋める書き込み専用のコントラクトです。Dart Documentation, 2025FutureはDart、Java、Scala、C++でthen/catchチェーンに使用されます。

要点

  • Future — 将来利用できる非同期結果の読み取り専用コンテナ
  • Promise — 結果を受け取りFutureに渡す書き込み専用のプレースホルダー
  • thenチェーン — 入れ子のコールバックを必要としない非同期処理の逐次処理
  • DartのFuture — async/awaitとdio、http、shelfライブラリの基盤
  • Future/Promise — コールバックパターンの代替であり、async/await構文の基盤

Future/Promiseとは何か?

Future/Promiseは、非同期の結果を二つの役割に分ける相互に関連したオブジェクトのペアです。Promiseは結果(resolve)またはエラー(reject)を書き込むことができます。Futureはそれを読み取り専用で参照できるだけで、コールバック(then、catchError)またはawaitを通じて結果を利用します。この分離により、利用者は結果を変更できず、参照のみが可能であることが保証されます。

DartにおけるFuture

Dart言語はFutureを主要な非同期メカニズムとして使用します。Future<T>は将来T型の値を取得するためのコントラクトです。Futureには三つの状態があります:未解決(処理の実行中)、値付きで解決(正常完了)、エラー付きで解決(エラーで完了)。解決済みの状態になるとFutureは変化しません。

JavaScriptにおけるPromise

PromiseはJavaScriptのFutureに相当しますが、より豊富なAPIを備えています。Promiseはエグゼキューター(resolveとrejectを引数に取る関数)を受け取ります。静的メソッドのPromise.all、Promise.race、Promise.allSettledは複数のプロミスを組み合わせることを可能にします。現代のアプリケーションはPromiseの上でasync/await構文を使用します。

Futureの仕組み

Futureはイベントループ(メインスレッドをブロックせずに非同期処理を扱うイベントの循環)に基づいて動作します。非同期関数(たとえばhttp.get)を呼び出すと、すぐにFutureが返されます。実際のリクエストはバックグラウンドで実行され、イベントループは他のイベントを処理し続けます。処理が完了するとFutureは解決済みの状態になり、thenが呼び出されます。

イベントループはDartとJavaScriptが構築された基盤となる仕組みです。イベントループは二つのキューを保持します:マイクロタスクキュー(次のイベントの前に実行される短いタスク)とイベントキュー(IOイベント、タイマー)。Future.thenはコールバックをマイクロタスクキューに置き、Future.microtaskは次のフレームまでに優先的に実行されます。

then()チェーンは、コールバック地獄を避けながら非同期処理の結果を順番に処理することを可能にします。各then()は前のステップの結果を受け取り、新しいFutureを返します。チェーンの末尾のcatchError()は、どのステップで発生したエラーも処理します。これにより非同期コードは直線的で読みやすくなります。

JavaにおけるCompletableFuture

JavaではCompletableFutureがFuture/Promiseの概念を拡張します。supplyAsync()メソッドはForkJoinPool.commonPool()で非同期タスクを開始します。thenApply()は結果を変換し、thenCompose()は入れ子になったCompletableFutureを展開します。exceptionally()はデフォルト値を返してエラーを処理します。allOf()とanyOf()はJavaScriptのPromise.allとPromise.raceのように動作します。CompletableFutureはcomplete()とcompleteExceptionally()による手動完了をサポートします — これはPromiseのresolve/rejectに相当します。

Promiseと非同期コントラクト

Promiseは三つの状態のいずれかを持つコントラクトです:pending(待機中)、fulfilled(正常完了)、rejected(エラーで完了)。fulfilledまたはrejectedの状態になるとPromiseは二度と変化しません — これにより、すべての利用者に対して結果の不変性が保証されます。.then()メソッドは、いずれかの完了状態に遷移した後に非同期で呼び出されます。

Promise.allは渡されたすべてのプロミスの完了を待ち、同じ順序で結果の配列を返します。いずれかのプロミスがエラーで完了すると、Promise.all全体がrejected状態になります。Promise.raceは最初に完了したプロミスの結果(成功またはエラー)を返します。Promise.allSettledはエラーに関係なくすべてのプロミスを待ち、それぞれのステータスを返します。

チェーン化はPromiseがコールバックより優れている主な点です。各.then()は新しいPromiseを返すため、任意の長さのチェーンを構築できます。チェーンのどの段階で発生したエラーも、最も近い.catch()まで伝播します。finally()メソッドは、結果に関係なくチェーン完了後にコードを実行します — リソースの解放に使用します。

Futureのコード例(Dart)

最初の例 — async関数によるFutureの作成とthen/catchErrorによる処理:

dart
Future<String> fetchData() async {
    await Future.delayed(Duration(seconds: 1))
    return "データを読み込みました"
}

fetchData()
    .then((result) => print(result))
    .catchError((error) => print("エラー: $error"))

二つ目の例 — Future.waitによる複数のFutureの組み合わせ:

dart
Future<List<String>> loadAll() async {
    final futures = [
        fetchFromApi("/users"),
        fetchFromApi("/posts"),
        fetchFromApi("/comments")
    ]
    final results = await Future.wait(futures)
    return results
}

三つ目の例 — JavaScriptで手動で作成しthenチェーンを使ったPromise:

js
const promise = new Promise((resolve, reject) => {
    setTimeout(() => {
        resolve("完了")
    }, 1000)
})

promise
    .then(value => console.log(value))
    .catch(err => console.error(err))

Futureと他の非同期パターン

Future/Promiseは、コールバック関数を引数として受け取る代わりにコントラクトオブジェクトを返す点でコールバック方式と異なります。コールバック方式はネスト(コールバック地獄)を招きますが、Futureは平坦なthen()チェーンを構築できます。async/await構文はFutureを使ったコードをさらに直線的で、同期処理に似たものにします。

Futureはちょうど一つの非同期結果を表します — 一度のエミッションの後にFutureは完了します。Observable(Rx、ReactiveX)やFlow(Kotlin Coroutines)とは異なり、Futureは時間的に複数の値をサポートしません。イベントのストリームが必要な場合は、DartのStream、RxのObservable、KotlinのFlowを使用してください。

CompletableFutureはJavaにおけるPromiseの類似物で、拡張されたAPI(supplyAsync、thenApply、thenCompose、exceptionally)を備えています。CompletableFutureはallOf、anyOfによる複数の非同期処理の結合が可能で、独自のExecutorを指定して実行することもできます。ScalaのFutureはスレッドプールを管理するためにExecutionContextを使用し、for-comprehensionによる合成をサポートします。

DartのFutureパターンは、Dartが強い型付けを持つ点でJavaScriptのPromiseとは異なります:Future<String>は戻り値の型を保証します。Dartのasync関数はFutureを返す必要があり、コンパイラはすべての経路が正しい型を返すことを検証します。Future.waitはPromise.allに相当し、Iterable<Future<T>>を受け取ってFuture<List<T>>を返します。Future.delayedとFuture.errorは非同期シナリオのテストに便利なファクトリーです。

Future/PromiseはPythonではconcurrent.futures.Futureとasyncio.Futureで実装されています。concurrent.futures.ThreadPoolExecutor.submit()はFutureを返し、そのresult()を呼び出すとブロッキング呼び出しになります。asyncio.FutureはAsyncioにおける非同期結果のawait互換コンテナです。asyncio.gather()は複数のコルーチンを並列に実行し、JavaScriptのPromise.allと同様に結果のリストを返します。Pythonではコールバックベースのasyncio.ensure_futureやadd_done_callbackチェーンも非同期処理の合成に使用されますが、現代のコードではasyncio.Taskを用いたasync/await構文が好まれます。

モバイル開発ではFutureはFlutterアプリで積極的に使用されています。httpやdioパッケージの各ネットワークリクエストはFutureを返します。FutureBuilder — Futureと状態のスナップショット(connectionDone、hasData、hasError)を受け取るウィジェットです。データを受け取るとsnapshot.dataにFutureの結果が含まれます。StreamBuilderは複数の値に対応する類似物です。FutureBuilderはsetStateによる手動の読み込み状態管理を不要にします。

ScalaのFutureはExecutionContextを暗黙的に使用します — 非同期タスクが実行されるスレッドプールです。Futureはモナドであるため、map、flatMap、filterによる合成が可能です。for-comprehensionはネストなしで複数のFutureを順番に組み合わせることができます:for { a <- futureA; b <- futureB } yield a + b。Future.sequenceはPromise.allのように動作します。エラー時のフォールバックはrecoverとrecoverWithで実装されます。

Future/PromiseはC++では<future>ライブラリのstd::futureクラスとstd::promiseクラスで表されます。std::asyncは非同期関数を起動し、std::futureを返します。std::promiseはset_value()による値の設定やset_exception()によるエラーの設定を手動で行えます。DartやJavaScriptとは異なり、std::future.get()は結果が得られるまでスレッドをブロックします — これはC++でマルチスレッドアプリケーションを設計する際に考慮すべき重要な違いです。

Futureの活用パターン

Futureを返すAPIを設計する際は、単一情報源のルールに従ってください:各非同期リクエストは一度だけ作成され、Future.memoizeでキャッシュされます。二つのコンポーネントが同じデータを要求する場合、同じFutureを受け取るため、リクエストの重複が防止されます。タイムアウトにはFuture.timeout(Dart)またはタイムアウト付きのPromise.raceを使用し、非同期処理が指定された間隔内に完了することを保証してください。

リトライパターンはFutureでは再帰チェーンで実装されます:エラー時にcatchで同じ非同期関数を遅延付きで呼び出します。指数バックオフは試行間の間隔を増やします:1秒 — 2秒 — 4秒 — 最大30秒。DartではFutureの拡張(extension)にカプセル化されます:future.retry(maxRetries: 3, delay: Duration(seconds: 1))。ScalaのFuture.zipは二つの独立した結果をタプルに結合します — 二つのFutureに対するPromise.allの代替です。キャンセルは標準のFutureではサポートされていませんが、CancellationTokenまたはJavaScriptのAbortControllerで実装できます。

よくある質問

FutureとPromiseの違いは何ですか?

Futureはthenまたはawaitを通じて非同期結果を読み取るための読み取り専用コンテナです。Promiseはresolve()で値を受け取り、それをFutureに結び付ける書き込み専用のコントラクトです。DartとJavaScriptではこれらの概念は統合されており、Future/Promiseは一つのクラスです。JavaとScalaでは分離されています。

FutureはObservableとどう違いますか?

Futureは単一の非同期結果を表し、ちょうど一つの値をエミットして完了します。Observable(Rx)は時間の経過に応じて複数の値をエミットします — これは非同期ストリームです。単一のHTTPレスポンスが必要ならFutureを使用してください。データベースの変更ストリームが必要ならObservableまたはStreamを使用してください。

async/awaitとは何で、Futureとどう関係しますか?

async/awaitはFuture/Promiseの構文糖衣(シンタックスシュガー)です。asyncキーワードは関数を非同期としてマークし、その関数はFutureを返します。awaitはスレッドをブロックせずにFutureの完了まで実行を一時停止します。Dart、JavaScript、Python、C#では、async/awaitはほとんどのシナリオでthenチェーンに取って代わりました。

Futureチェーンでエラーを処理するにはどうすればよいですか?

Dartではチェーンの末尾で.catchError()を使用してください — どの段階で発生した例外も捕捉します。JavaScriptでは.catch()を使用します。Dartではasync関数内でtry/await/catchも利用できます。エラー後の復旧には、デフォルト値を返す.catchError()または新しいFutureを使用してください。

Promise.allとは何で、いつ使用すればよいですか?

Promise.allはプロミスの配列を受け取り、結果の配列で解決される単一のPromiseを返します。すべてのプロミスは並列に実行されるため、独立した処理(複数ファイルの読み込みなど)を高速化できます。いずれかのプロミスが拒否されると、Promise.all全体が同じエラーで拒否されます。

まとめ

  • Future/Promise — 単一の値に対するコントラクトを持つ非同期プログラミングのパターン
  • Future — 処理結果を読み取るための読み取り専用コンテナ
  • Promise — resolve/rejectでFutureを埋める書き込み専用コントラクト
  • thenチェーン — コールバック地獄のない非同期ステップの平坦な処理
  • async/await — コードを直線的にするFutureの構文糖衣
  • Promise.all — 複数の非同期処理の並列実行
  • 時間的に複数の値が必要ならFutureではなくStreamまたはObservableを使用

ターンキー方式のモバイルアプリケーションを開発します

IT Sectrは2017年からスタートアップや企業向けにiOS・Androidアプリケーションを開発しています。私たちがご相談に乗り、最適なソリューションをご提案します。

プロジェクトについて相談

こちらもお読みください