jQuery deferredの「then」メソッドをいつ使用する必要がありますか。「pipe」メソッドをいつ使用する必要がありますか?


97

jQueryにDeferredは、関数の非同期チェーンを実装するために使用できる2つの関数があります。

then()

deferred.then( doneCallbacks, failCallbacks ) Returns: Deferred

doneCallbacks Deferredが解決されたときに呼び出される関数または関数の配列。
failCallbacks Deferredが拒否されたときに呼び出される関数または関数の配列。

pipe()

deferred.pipe( [doneFilter] [, failFilter] ) Returns: Promise

doneFilter Deferredが解決されたときに呼び出されるオプションの関数。
failFilter Deferredが拒否されたときに呼び出されるオプションの関数。

then()はもう少し長いことを知っているpipe()ので、後者はいくつかの追加の利点を追加する必要がありますが、その違いは正確にはわかりません。名前は異なりますが、a Deferredを返すことPromiseとa を返すことの違いはわずかに見えますが、どちらもほとんど同じコールバックパラメータを取ります。

私は何度も公式ドキュメントを読みましたが、常に「密度が高すぎて」本当に頭を抱えこむことができず、検索で1つの機能または他の機能に関する多くの議論が見つかりましたが、違いを明確にするものは何も見つかりませんでした。それぞれの長所と短所。

それで、いつ使用thenするのが良いのpipeですか、いつ使用するのが良いのですか?


添加

Felixの優れた答えは、これら2つの機能の違いを明確にするのに役立ちました。しかし、の機能がの機能よりthen()も望ましい場合があるのでしょうかpipe()

pipe()がより強力であることは明らかでthen()、前者は後者ができることは何でもできるようです。使用する理由の1つはthen()、その名前が、同じデータを処理する一連の関数の終了としての役割を反映していることです。

しかし、新しいものを返すために実行できないthen()元のものを返す必要があるユースケースはありますか?Deferredpipe()Promise


1
しばらく考えましたが、ユースケースなんて考えられません。必要がない場合は、新しいpromiseオブジェクトを作成するだけでオーバーヘッドになる可能性があります(内部的にどのようにチェーンされているのかわかりません)。そうは言っても、私よりもこれをよく理解している人は確かにいます。
Felix Kling

6
この質問に興味がある人は確かにjQueryのバグトラッカーにチケット#11010に興味があるだろう:MAKE DEFERRED.THEN == DEFERRED.PIPE LIKE PROMISE / A
ヒッピー・トレイル

回答:


103

以来jQueryの1.8 .thenと同じように動作します.pipe

非推奨の通知: jQuery 1.8以降、このdeferred.pipe()メソッドは非推奨になりました。deferred.then()代わりに、このメソッドを使用する必要があります。

そして

jQuery 1.8以降、このdeferred.then()メソッドは、関数を介して遅延オブジェクトのステータスと値をフィルタリングできる新しいpromiseを返し、非推奨となったdeferred.pipe()メソッドを置き換えます。

以下の例は、まだいくつかの人に役立つかもしれません。


それらは異なる目的を果たします:

  • .then()ドキュメントの説明のように、遅延オブジェクトが解決または拒否されたときに、プロセスの結果を操作する場合に常に使用されます。.done()またはを使用するのと同じ.fail()です。

  • 何らかの方法で結果.pipe()を(事前に)フィルタリングするために使用します。へのコールバックの戻り値は.pipe()doneおよびfailコールバックに引数として渡されます。また、別の遅延オブジェクトを返すこともでき、次のコールバックがこの遅延オブジェクトに登録されます。

    これは.then()(または.done().fail())の場合とは異なり、登録されたコールバックの戻り値は無視されます。

つまり .then() または を使用することではありません.pipe()。君は可能性が使用し.pipe()て同じ目的のために.then()その逆は成り立ちません。


例1

いくつかの操作の結果はオブジェクトの配列です:

[{value: 2}, {value: 4}, {value: 6}]

値の最小値と最大値を計算したいとします。2つ使用すると仮定しましょうdoneコールバックます。

deferred.then(function(result) {
    // result = [{value: 2}, {value: 4}, {value: 6}]

    var values = [];
    for(var i = 0, len = result.length; i < len; i++) {
        values.push(result[i].value);
    }
    var min = Math.min.apply(Math, values);

   /* do something with "min" */

}).then(function(result) {
    // result = [{value: 2}, {value: 4}, {value: 6}]

    var values = [];
    for(var i = 0, len = result.length; i < len; i++) {
        values.push(result[i].value);
    }
    var max = Math.max.apply(Math, values);

   /* do something with "max" */ 

});

どちらの場合も、リストを反復処理して、各オブジェクトから値を抽出する必要があります。

両方のコールバックで個別にこれを行う必要がないように、何らかの方法で事前に値を抽出する方がよいのではないでしょうか。はい!そして、それは私たちが使用できるもの.pipe()です:

deferred.pipe(function(result) {
    // result = [{value: 2}, {value: 4}, {value: 6}]

    var values = [];
    for(var i = 0, len = result.length; i < len; i++) {
        values.push(result[i].value);
    }
    return values; // [2, 4, 6]

}).then(function(result) {
    // result = [2, 4, 6]

    var min = Math.min.apply(Math, result);

    /* do something with "min" */

}).then(function(result) {
    // result = [2, 4, 6]

    var max = Math.max.apply(Math, result);

    /* do something with "max" */

});

明らかにこれは作り上げの例であり、この問題を解決するための多くの異なる(おそらくより良い)方法がありますが、それが要点を示していることを願っています。


例2

Ajax呼び出しを検討してください。前の呼び出しが完了した後で、1つのAjax呼び出しを開始したい場合があります。1つの方法は、doneコールバック内で2番目の呼び出しを行うことです。

$.ajax(...).done(function() {
    // executed after first Ajax
    $.ajax(...).done(function() {
        // executed after second call
    });
});

ここで、コードを分離して、これら2つのAjax呼び出しを関数内に配置したいとします。

function makeCalls() {
    // here we return the return value of `$.ajax().done()`, which
    // is the same deferred object as returned by `$.ajax()` alone

    return $.ajax(...).done(function() {
        // executed after first call
        $.ajax(...).done(function() {
            // executed after second call
        });
    });
}

遅延オブジェクトを使用して、呼び出しmakeCallsを行う他のコードが2番目の Ajax呼び出しのコールバックをアタッチできるようにしたいが、

makeCalls().done(function() {
    // this is executed after the first Ajax call
});

2番目の呼び出しはdoneコールバック内で行われ、外部からアクセスできないため、目的の効果は得られません。

解決策は.pipe()代わりに使用することです:

function makeCalls() {
    // here we return the return value of `$.ajax().pipe()`, which is
    // a new deferred/promise object and connected to the one returned
    // by the callback passed to `pipe`

    return $.ajax(...).pipe(function() {
        // executed after first call
        return $.ajax(...).done(function() {
            // executed after second call
        });
    });
}

makeCalls().done(function() {
    // this is executed after the second Ajax call
});

を使用.pipe()することで、コールの実際のフロー/順序を公開することなく、「内部」Ajaxコールにコールバックを追加できるようになります。


一般的に、遅延オブジェクトはコードを分離する興味深い方法を提供します:)


ああ、そうでpipeはないフィルタリングを実行できることを見落としましたthen。しかし、これらのトピックをググリングする際には、がフィルタリングを付属のボーナスのようなものであると見なし、その真の目的をより明確に示しているためでpipeはなく、それを呼び出すことを選択したようです。したがって、フィルタリング以外にも他の違いがあるはずです。(その後、再び私は本当にもあなたの例によるフィルタリング機能を理解していない認めるべき。可能な方法で?)filterpiperesult values;return values;
ヒッピー・トレイル

私があなたの例を理解していないと言うとき、それはこのようなものですか?上の例では、2 .then()つのは、result毎回フィルタリングする同じデータを受け取ります。一方、下の例では、.pipe()は後続の2つのが受信resultするので、それを渡す前にのデータの一部を削除しますか?result.then()
ヒッピートレイル2012年

1
@hippietrail:その間、私は回答を更新し、の他の目的も含めました.pipe()。コールバックが遅延オブジェクトを返す場合、その後の完了または失敗のコールバックがそのオブジェクトに登録されます。別の例を示します。編集: 2番目のコメントについて:はい。
Felix Kling

したがって、1つの違いは、データが通過するの pipe()に対し、最後にデータを使用する必要があるリーフノードのthen()ようなものであり、それ以上フローしないこと、およびを返すという事実にもかかわらず、実際に使用/使用されないことです。これが正しい場合は、各 ".then()句"のようなものを含めることを明確にすることが役立つ場合があります。then()Deferred/* do something with "min"/"max" */
ヒッピートレイル2012年

1
心配する必要はありません:)遅延オブジェクトとそのメソッドがどのように機能するかを完全に理解するのにも少し時間がかかりました。しかし、あなたがそれを理解すれば、それはもはや難しいようには見えません。ドキュメントはおそらくもっと簡単な方法で書くことができるだろうと私は同意します。
Felix Kling

7

then()overを使用しなければならないケースはありませんpipe()pipe()渡される値は常に無視するように選択できます。使用すると、パフォーマンスにわずかな影響が出る可能性ありますpipe問題になることはほとんどありません。

したがってpipe()、どちらの場合でも常に使用できるように思えるかもしれません。ただし、を使用するpipe()ことで、コードを読んでいる他の人(自分自身を含む、6か月後)に、戻り値が重要であることを伝えます。これを破棄する場合は、このセマンティックコンストラクトに違反しています。

これは、決して使用されない値を返す関数があるようなものです。

だから、必要なthen()ときに、そしてpipe()必要なときに...


3
K.スコットアレンのブログ「Experiments In Writing」の2つを使用した実際の例を見つけました。Geolocation、Geocoding、jQueryの約束:「それでは、制御ロジックはかなりうまくいきます」:「 $(function () { $.when(getPosition()) .pipe(lookupCountry) .then(displayResults); }); パイプはパイプが新しい約束を返すからです。」
ヒッピートレイル2012

5

実際には違いことが判明.then()し、.pipe()不要と考えられていると、彼らはjQueryのバージョン1.8のと同じになるように行われています。

jQueryのバグトラッカーチケット#11010「MAKE DEFERRED.THEN == DEFERRED.PIPE LIKE PROMISE / A」のコメントjaubourgから:

1.8では、古いものを削除し、現在のパイプに置き換えます。しかし、非常に悲しい結果は、非標準のdone、fail、progressを使用するように人々に指示する必要があるということです。なぜなら、この提案は単純で効率的ではなく、単にコールバックを追加するだけだからです。

(強調鉱山)


1
これまでのところ最高のリファレンスです。高度な使用法を探しています。
TWiStErRob 2013
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.