Promise

Promise オブジェクトは非同期処理の最終的な完了処理(もしくは失敗)およびその結果の値を表現します。

Promise の挙動と使用法について学ぶには、最初に Promise の使用をお読みください。

解説

Promise インターフェイスは作成時点では分からなくてもよい値へのプロキシーです。Promise を用いることで、非同期アクションが最終的に成功した時の値や失敗した時の理由に対するハンドラーを関連付けることができます。これにより、非同期メソッドは、最終的な値をすぐに返す代わりに、未来のある時点で値を持つ Promise を返すことで、同期メソッドと同じように値を返すことができるようになります。

Promise の状態は以下のいずれかとなります。

  • pending: 初期状態。成功も失敗もしていません。
  • fulfilled: 処理が成功して完了したことを意味します。
  • rejected: 処理が失敗したことを意味します。

pending 状態の Promise は、何らかの値を持つ fulfilled 状態、もしくは何らかの理由 (エラー) を持つ rejected 状態のいずれかに変わります。そのどちらとなっても、then メソッドによって関連付けられたハンドラーが呼ばれます。(対応するハンドラーがアタッチされたとき、既に Promise が成功または失敗していても、そのハンドラーは呼ばれます。よって、非同期処理とその関連付けられたハンドラーとのレースコンディションは発生しません。)

Promise.prototype.then() メソッドと Promise.prototype.catch() メソッドもまた Promise を返すので、これらをチェーン (連鎖) させることができます。

混乱を避けるために: Scheme に代表されるいくつかの言語では、遅延評価や計算を延期する機構を持っており、これらも "Promise" と呼ばれます。JavaScript における Promise は、すでに起きつつある処理を表します。そしてこの処理はコールバックを使うことでチェーンさせることができます。式を遅延評価する方法を探しているのであれば、引数なしのアロー関数を考えてください。f = () => expression のように実現でき、遅延評価される式が作成され、f() を呼ぶことでその式を評価できます。

メモ: Promise は fulfilled failed のどちらかになった場合は、pending ではなく settled と呼ばれます。また解決 (resolved) という用語も目にされたことがあると思います。解決とは、Promise が解決または他の promise の状態にマッチするために" locked in "したことを意味します。States and fates では、Promise の技術についてより詳細に述べられています。

コンストラクタ

Promise()
コンストラクタは主に、まだ Promise をサポートしていない関数をラップするのに使用されます。

プロパティ

Promise.length
長さを表し、常に 1 (コンストラクターの引数の数)。
Promise.prototype
Promise コンストラクターのプロトタイプを表す。

メソッド

Promise.all(iterable)
すべての Promise が解決されるか、いずれかが拒否されるかするまで待ちます。
返却される Promise が解決された場合、解決された Promise が、複数の Promise が含まれる iterable で定義された通りの順番で入った集合配列の値によって解決されます。
拒否された場合は、iterable の中で拒否された最初の Promise の理由によって拒否されます。
Promise.allSettled(iterable)
すべての Promise が完了する (それぞれが解決するか、拒否される) まで待ちます。
すべての与えられた Promise が解決または拒否された後で、それぞれの Promise の結果を記述するオブジェクトの配列で解決されるPromiseを返します。
Promise.race(iterable)
Promise のうちの 1 つが解決または拒否されるまで待ちます。
返された Promise が解決された場合、iterable の中で最初に解決された Promise の値によって解決されます。
拒否された場合、最初に拒否された Promise の理由によって拒否されます。
Promise.reject(reason)
与えられた理由で拒否された新しい Promise オブジェクトを返します。
Promise.resolve(value)
与えられた値で解決された新しい Promise オブジェクトを返します。もし値が thenable (つまり then メソッドを持っているオブジェクト) ならば、返される Promise はその thenable をたどり、その結果を採用します。そうでなければ、返される Promise は与えられた値で解決されます。
一般に、ある値が Promise かどうかがわからない場合は、Promise.resolve(value) を使って Promise にして扱います。

Promise プロトタイプ

プロパティ

Promise.prototype.constructor
インスタンスのプロトタイプを作成した関数を返します。既定では. Promise 関数です。

メソッド

Promise.prototype.catch()
Promise に rejection ハンドラーのコールバックを追加し、コールバックが呼ばれた場合はその戻り値、または Promise が fulfill された場合は、元の fulfillment 値を解決する新しい Promise を返します。
Promise.prototype.then()
Promise に fulfillment と rejection ハンドラーを追加し、呼び出されたハンドラーの戻り値、または Promise が処理されなかった場合は (つまり、関連する onFulfilledonRejected ハンドラーが関数でない場合)元の決められた値で解決される新しい Promise を返します。
Promise.prototype.finally()
promise にハンドラーを追加し、元の Promise が解決された時に返される新しい Promise を返します。ハンドラーは Promise が決定した場合は、fulfilled または rejected いずれでも呼び出されます。

基本的な使用例

let myFirstPromise = new Promise((resolve, reject) => {
  // We call resolve(...) when what we were doing asynchronously was successful, and reject(...) when it failed.
  // In this example, we use setTimeout(...) to simulate async code. 
  // In reality, you will probably be using something like XHR or an HTML5 API.
  setTimeout(function(){
    resolve("Success!") // Yay! Everything went well!
  }, 250)
});

myFirstPromise.then((successMessage) => {
  // successMessage is whatever we passed in the resolve(...) function above.
  // It doesn't have to be a string, but if it is only a succeed message, it probably will be.
  console.log("Yay! " + successMessage)
});

応用例

以下の例は Promise の仕組みを示したものです。testPromise() メソッドは <button> をクリックする度に呼び出されます。testPromise() メソッドは、window.setTimeout() を用いて、1秒から 3秒のランダムな時間の後、メソッドがこれまでに呼ばれた回数で成功する Promise を作成します。Promise() コンストラクターは Promise を作成するために使用されます。

Promise の成功は、p1.then() で設定されたコールバックによって記録されます。この記録から、メソッドの同期処理部分が、Promise による非同期処理からどのように分離されているかがわかります。

'use strict';
var promiseCount = 0;

function testPromise() {
    let thisPromiseCount = ++promiseCount;

    let log = document.getElementById('log');
    log.insertAdjacentHTML('beforeend', thisPromiseCount +
        ') 開始 (<small>同期処理開始</small>)<br/>');

    // 新しい Promise を作成: 1~3秒後に結果を返すことを約束します
    let p1 = new Promise(
        // executor 関数は Promise の成功または失敗に応じて呼ばれます
       (resolve, reject) => {
            log.insertAdjacentHTML('beforeend', thisPromiseCount +
                ') Promise 開始 (<small>非同期処理開始</small>)<br/>');
            // 非同期を作成するための一例です
            window.setTimeout(
                function() {
                    // 約束を果たしました!
                    resolve(thisPromiseCount);
                }, Math.random() * 2000 + 1000);
        }
    );

    // Promise が成功した時に何をするかを定めます then() で成功した時
    // catch() で失敗した時
    p1.then(
        // メッセージと値を記録します
        function(val) {
            log.insertAdjacentHTML('beforeend', val +
                ') Promise 成功 (<small>非同期処理終了</small>)<br/>');
        }).catch(
        // 失敗した理由を記録します
       (reason) => {
            console.log('Handle rejected promise ('+reason+') here.');
        });

    log.insertAdjacentHTML('beforeend', thisPromiseCount +
        ') Promise は作成されました (<small>同期処理終了</small>)<br/>');
}

この例はボタンをクリックすると実行されます。(ブラウザーが Promise に対応している必要があります。)

短い時間の間に何度かボタンをクリックすると、それぞれの promise が次々と成功するのがわかります。

XHR による画像の読み込み

PromiseXMLHttpRequest で画像を読み込む別の例は、MDN GitHub js-examples リポジトリにあり、動作を確認することができます。それぞれの行のコメントで Promise と XHR の構造がよくわかるはずです。

仕様書

仕様書
ECMAScript Latest Draft (ECMA-262)
Promise の定義

ブラウザーの互換性

Update compatibility data on GitHub
デスクトップモバイルサーバー
ChromeEdgeFirefoxInternet ExplorerOperaSafariAndroid webviewAndroid 版 ChromeAndroid 版 FirefoxAndroid 版 OperaiOSのSafariSamsung InternetNode.js
PromiseChrome 完全対応 32Edge 完全対応 12Firefox 完全対応 29IE 未対応 なしOpera 完全対応 19Safari 完全対応 8WebView Android 完全対応 4.4.3Chrome Android 完全対応 32Firefox Android 完全対応 29Opera Android 完全対応 19Safari iOS 完全対応 8Samsung Internet Android 完全対応 2.0nodejs 完全対応 0.12
Promise() constructorChrome 完全対応 32Edge 完全対応 12Firefox 完全対応 29
補足
完全対応 29
補足
補足 Constructor requires a new operator since version 37.
IE 未対応 なしOpera 完全対応 19Safari 完全対応 8
補足
完全対応 8
補足
補足 Constructor requires a new operator since version 10.
WebView Android 完全対応 4.4.3Chrome Android 完全対応 32Firefox Android 完全対応 29
補足
完全対応 29
補足
補足 Constructor requires a new operator since version 37.
Opera Android 完全対応 19Safari iOS 完全対応 8
補足
完全対応 8
補足
補足 Constructor requires a new operator since version 10.
Samsung Internet Android 完全対応 2.0nodejs 完全対応 0.12
補足
完全対応 0.12
補足
補足 Constructor requires a new operator since version 4.
all()Chrome 完全対応 32Edge 完全対応 12Firefox 完全対応 29IE 未対応 なしOpera 完全対応 19Safari 完全対応 8WebView Android 完全対応 4.4.3Chrome Android 完全対応 32Firefox Android 完全対応 29Opera Android 完全対応 19Safari iOS 完全対応 8Samsung Internet Android 完全対応 2.0nodejs 完全対応 0.12
allSettled()Chrome 完全対応 76Edge 完全対応 79Firefox 完全対応 71IE 未対応 なしOpera 完全対応 63Safari 完全対応 13WebView Android 完全対応 76Chrome Android 完全対応 76Firefox Android 未対応 なしOpera Android 完全対応 54Safari iOS 完全対応 13Samsung Internet Android 未対応 なしnodejs 完全対応 12.9.0
catch()Chrome 完全対応 32Edge 完全対応 12Firefox 完全対応 29IE 未対応 なしOpera 完全対応 19Safari 完全対応 8WebView Android 完全対応 4.4.3Chrome Android 完全対応 32Firefox Android 完全対応 29Opera Android 完全対応 19Safari iOS 完全対応 8Samsung Internet Android 完全対応 2.0nodejs 完全対応 0.12
finally()Chrome 完全対応 63Edge 完全対応 18Firefox 完全対応 58IE 未対応 なしOpera 完全対応 50Safari 完全対応 11.1WebView Android 完全対応 63Chrome Android 完全対応 63Firefox Android 完全対応 58Opera Android 完全対応 46Safari iOS 完全対応 11.3Samsung Internet Android 完全対応 8.0nodejs 完全対応 10.0.0
race()Chrome 完全対応 32Edge 完全対応 12Firefox 完全対応 29IE 未対応 なしOpera 完全対応 19Safari 完全対応 8WebView Android 完全対応 4.4.3Chrome Android 完全対応 32Firefox Android 完全対応 29Opera Android 完全対応 19Safari iOS 完全対応 8Samsung Internet Android 完全対応 2.0nodejs 完全対応 0.12
reject()Chrome 完全対応 32Edge 完全対応 12Firefox 完全対応 29IE 未対応 なしOpera 完全対応 19Safari 完全対応 8WebView Android 完全対応 4.4.3Chrome Android 完全対応 32Firefox Android 完全対応 29Opera Android 完全対応 19Safari iOS 完全対応 8Samsung Internet Android 完全対応 2.0nodejs 完全対応 0.12
resolve()Chrome 完全対応 32Edge 完全対応 12Firefox 完全対応 29IE 未対応 なしOpera 完全対応 19Safari 完全対応 8WebView Android 完全対応 4.4.3Chrome Android 完全対応 32Firefox Android 完全対応 29Opera Android 完全対応 19Safari iOS 完全対応 8Samsung Internet Android 完全対応 2.0nodejs 完全対応 0.12
then()Chrome 完全対応 32Edge 完全対応 12Firefox 完全対応 29IE 未対応 なしOpera 完全対応 19Safari 完全対応 8WebView Android 完全対応 4.4.3Chrome Android 完全対応 32Firefox Android 完全対応 29Opera Android 完全対応 19Safari iOS 完全対応 8Samsung Internet Android 完全対応 2.0nodejs 完全対応 0.12

凡例

完全対応  
完全対応
未対応  
未対応
実装ノートを参照してください。
実装ノートを参照してください。

関連情報