web-dev-qa-db-ja.com

JavaScriptのDeferred、Promise、Futureの違いは何ですか?

繰延、約束、先物の違いは何ですか?
これら3つすべての背後に一般に認められている理論はありますか?

290
Tower

OPの質問に私がどのように答えようとしたかに対する明らかな嫌悪感に照らして。文字通りの答えは、promiseは他のオブジェクトと共有されるものであり、deferredは非公開にする必要があるということです。主に、遅延(一般にPromiseを拡張する)は解決できますが、Promiseは解決できない場合があります。

特徴に興味がある場合は、 Promises/A + を調べてください。


私が知っている限りでは、最も重要な目的は、標準化されたインターフェースを介して明快さを改善し、結合を緩めることです。 推奨読書 @ jfriend00から:

Promiseを使用すると、コールバックを関数に直接渡すのではなく、密結合インターフェースにつながる可能性があるため、同期または非同期のコードに対する懸念を分離できます。

個人的に、私は、たとえば、非同期リクエストによって生成されるテンプレート、依存関係のネットワークを持つスクリプトのロード、および非ブロック方式でデータを形成するためのユーザーフィードバックの提供。

確かに、JSモードでCodeMirrorを非同期にロードした後に何かを行う純粋なコールバック形式を比較してください(申し訳ありませんが、whileでjQueryを使用していません):

/* assume getScript has signature like: function (path, callback, context) 
   and listens to onload && onreadystatechange */
$(function () {
   getScript('path/to/CodeMirror', getJSMode);

   // onreadystate is not reliable for callback args.
   function getJSMode() {
       getScript('path/to/CodeMirror/mode/javascript/javascript.js', 
           ourAwesomeScript);
   };

   function ourAwesomeScript() {
       console.log("CodeMirror is awesome, but I'm too impatient.");
   };
});

Promisesの定式化されたバージョン(再度、謝罪しますが、jQueryについては最新ではありません):

/* Assume getScript returns a promise object */
$(function () {
   $.when(
       getScript('path/to/CodeMirror'),
       getScript('path/to/CodeMirror/mode/javascript/javascript.js')
   ).then(function () {
       console.log("CodeMirror is awesome, but I'm too impatient.");
   });
});

準擬似コードについてはおologiesびしますが、コアアイデアが多少明確になることを願っています。基本的に、標準化されたプロミスを返すことで、プロミスを渡すことができるため、より明確なグループ化が可能になります。

94
fncomp

選択された答えを含むこれらの答えは、約束を概念的に紹介するのに適していますが、それらを実装するライブラリを使用するときに生じる用語の正確な違いの詳細に欠けています(そしてそこにがある重要な違い)。

まだ 進化中の仕様 であるため、答えは現在( wikipedia のような)参照と jQuery のような実装の両方を調査しようとすることから来ています:

  • Deferred:一般的な参考文献には記載されていませんが、 124 ただし、プロミス解決のアービターとして実装でよく使用されます(resolveおよびrejectの実装)。 567

    場合によっては、遅延も約束(thenの実装)であり、 56 それ以外の場合は、Deferredのみが解決可能で、ユーザーがthenを使用するためのプロミスにアクセスすることを強制する方がより純粋であると見なされます。 7

  • Promise:議論中の戦略にとって最も包括的なWordです。

    同期性を抽象化したいターゲット関数の結果を保存するプロキシオブジェクト。さらに、別のターゲット関数を受け入れて新しいプロミスを返すthen関数を公開します。 2

    CommonJS の例:

    > asyncComputeTheAnswerToEverything()
        .then(addTwo)
        .then(printResult);
    44
    

    責任解決が誰に当てはまるかについては指定されていませんが、一般的な参考文献に常に記載されています。 124

    一般的な実装では常に存在し、解決能力は与えられません。 567

  • Future:いくつかの一般的な参考文献に見られる、廃止予定の用語 1 少なくとも1つの一般的な実装、 8 しかし、「約束」という用語を優先して議論から段階的に削除されているようです  トピックの一般的な紹介で常に言及されているわけではありません。 9

    ただし、少なくとも1つのライブラリは、then機能を提供せずに、同期性とエラー処理を抽象化するために総称的にこの用語を使用します。 1 「約束」という用語を避けることが意図的かどうかは不明ですが、約束は「thenables」を中心に構築されるため、おそらく良い選択です。 2

参照資料

  1. 約束と未来に関するウィキペディア
  2. 約束/ A +仕様
  3. DOM Standard on Promises
  4. DOM Standard Promises Spec WIP
  5. DOJO Toolkit Deferreds
  6. jQuery Deferreds
  7. Q
  8. FutureJS
  9. Promiseの機能的なJavaScriptセクション
  10. AngularJS統合テストの将来

その他の潜在的に混乱させるもの

142
Woahdae

私にとって本当にクリックしたのは、Domenic Denicolaによる このプレゼンテーション でした。

github Gist で、彼は私が最も好きな説明を与えました、それは非常に簡潔です:

約束のポイントは、非同期の世界で機能的な構成とエラーバブリングを返すことです

つまり、Promiseは、synchronousと同じくらい簡単に記述できるasynchronousコードを記述できる方法です。

約束のあるこの例を考えてみましょう。

getTweetsFor("domenic") // promise-returning async function
    .then(function (tweets) {
        var shortUrls = parseTweetsForUrls(tweets);
        var mostRecentShortUrl = shortUrls[0];
        return expandUrlUsingTwitterApi(mostRecentShortUrl); // promise-returning async function
    })
    .then(doHttpRequest) // promise-returning async function
    .then(
        function (responseBody) {
            console.log("Most recent link text:", responseBody);
        },
        function (error) {
            console.error("Error with the twitterverse:", error);
        }
    );

この同期コードを書いているかのように動作します:

try {
    var tweets = getTweetsFor("domenic"); // blocking
    var shortUrls = parseTweetsForUrls(tweets);
    var mostRecentShortUrl = shortUrls[0];
    var responseBody = doHttpRequest(expandUrlUsingTwitterApi(mostRecentShortUrl)); // blocking x 2
    console.log("Most recent link text:", responseBody);
} catch (error) {
    console.error("Error with the twitterverse: ", error);
}

(これでも複雑に聞こえる場合は、そのプレゼンテーションを見てください!)

Deferredに関しては、.resolve()または.reject() promisesへの方法です。 Promises/B 仕様では、.defer()と呼ばれます。 jQueryでは、$.Deferred()です。

私の知る限り、少なくともjQuery 1.8.2の時点では、jQueryのPromise実装は壊れています(そのGistを参照)。
おそらく Promises/A thenables を実装していますが、「非同期のtry/catch」機能全体が機能しないという意味で、正しいエラー処理が得られません。 。非同期コードで「try/catch」を行うことは非常にクールだからです。

Promiseを使用する場合(独自のコードで試してみてください!)、 Kris Kowal's Q を使用します。 jQueryバージョンは、よりクリーンなjQueryコードを作成するためのコールバックアグリゲーターにすぎませんが、ポイントを逃しています。

Futureについては、どのAPIでも見たことがありません。

編集:Domenic Denicolaのyoutube on Promises from @ Farm のコメント.

ビデオからのマイケル・ジャクソンからの引用(はい、マイケル・ジャクソン):

このフレーズを心に焼き付けてほしい:約束は非同期値です。

これは素晴らしい説明です:約束は未来の変数のようなものです-ある時点で存在する(または起こる)ものへの第一級の参照です。

72
Camilo Martin

Promiseは、promiseの作成時に必ずしも認識されない値のプロキシを表します。これにより、ハンドラーを非同期アクションの最終的な成功値または失敗理由に関連付けることができます。これにより、非同期メソッドは同期メソッドのような値を返すことができます。非同期メソッドは、最終的な値の代わりに、将来のある時点で値を持つという約束を返します。

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise

deferred.promise()メソッドを使用すると、非同期関数は、他のコードがその内部リクエストの進行状況またはステータスに干渉するのを防ぐことができます。 Promiseは、追加のハンドラーのアタッチまたは状態の決定に必要なDeferredメソッドのみを公開します(then、done、fail、always、pipe、progress、state、promise )、ただし状態を変更するもの(resolve、reject、notify、resolveWith、rejectWith、およびnotifyWith).

ターゲットが提供される場合、deferred.promise()はメソッドをアタッチし、新しいオブジェクトを作成するのではなく、このオブジェクトを返します。これは、すでに存在するオブジェクトにPromiseの動作を添付するのに役立ちます。

Deferredを作成する場合は、Deferredへの参照を保持して、ある時点で解決または拒否できるようにします。 deferred.promise()を介してPromiseオブジェクトのみを返すので、他のコードはコールバックを登録したり、現在の状態を検査したりできます。

Promiseは、aDeferredは、まだ終了していない作業を表します。


enter image description here

29
IRSHAD
  • promiseは、まだ知られていない値を表します
  • deferredは、まだ終了していない作業を表します

約束は、最初は不明な結果のプレースホルダーであり、一方、遅延は値をもたらす計算を表します。

参照

21
mattLummus