web-dev-qa-db-ja.com

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

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

then()

deferred.then( doneCallbacks, failCallbacks ) Returns: Deferred

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

pipe()

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

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

then()pipe()よりも少し長くなっているので、後者はいくつかの追加の利点を追加する必要がありますが、正確な違いは私を免れます。両者はほぼ同じコールバックパラメータを取りますが、名前が異なり、Deferredを返すこととPromiseを返すことの違いはわずかです。

私は公式ドキュメントを何度も読みましたが、常に頭を包み込むにはあまりにも「密集」していることを見つけました。それぞれの長所と短所。

だから、いつthenを使用したほうがよいのか、いつpipeを使用したほうがよいのか?


添加

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

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

しかし、then()が元のDeferredを返す必要があるユースケースはありますが、pipe()ではできません。新しいPromiseを返すためです

95
hippietrail

jQuery 1.8.then.pipeと同じように動作するため:

廃止予定通知: jQuery 1.8では、deferred.pipe()メソッドは廃止されました。代わりにdeferred.then()メソッドを使用する必要があります。

そして

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

以下の例は、一部の人にとってはまだ役に立つかもしれません。


それらは異なる目的に役立ちます:

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

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

    .then()(または.done().fail())の場合はそうではありません。登録されたコールバックの戻り値は無視されます。

したがって、either.then()or.pipe()。あなたはcould.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を呼び出す他のコードがsecondAjax呼び出しのコールバックをアタッチできるようにしますが、

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コールにコールバックを追加できるようになりました。


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

102
Felix Kling

then()ではなくpipe()を使用する必要がある場合はありません。 pipe()が渡す値をいつでも無視することを選択できます。mightpipe-しかし、問題になる可能性は低いです。

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

それは、決して使用されない値を返す関数を持っているようなものです:紛らわしい。

したがって、必要な場合はthen()を使用し、必要な場合はpipe()を使用してください...

7
Alex Feinman

実際、.then().pipe()の違いは不要とみなされており、jQueryバージョン1.8と同じになっています。

jaubourgによるコメント jQueryのバグトラッカーで ticket#1101 "MAKE DEFERRED.THEN == DEFERRED.PIPE LIKE PROMISE/A":

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

(エンファシス鉱山)

5
hippietrail