我何时应该使用jQuery deferred的“then”方法,何时应该使用“pipe”方法?

时间:2012-03-06 12:32:52

标签: jquery asynchronous jquery-deferred decoupling jquery-chaining

jQuery的Deferred有两个函数可用于实现函数的异步链接:

then()

  

deferred.then( doneCallbacks, failCallbacks ) Returns: Deferred

     

doneCallbacks 解析延迟时调用的函数或函数数组。
   failCallbacks 拒绝延期时调用的函数或函数数组。

pipe()

  

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

     

doneFilter 解析Deferred时调用的可选函数    failFilter 拒绝延迟时调用的可选函数。

我知道then()已经比pipe()长了一点,所以后者必须增加一些额外的好处,但正是这种差异恰恰在于我。两者都采用了几乎相同的回调参数,尽管它们的名称不同,返回Deferred和返回Promise之间的区别似乎很小。

我已经一遍又一遍地阅读官方文档,但总是发现它们太“密集”而无法真正地包围我的搜索已经找到了很多关于这个功能或其他功能的讨论,但我还没有找到任何真正的功能澄清各自的不同利弊。

那么什么时候使用then更好?使用pipe什么时候更好?


加成

Felix's excellent answer确实帮助澄清了这两个功能的不同之处。但我想知道有时then()的功能优于pipe()的功能。

很明显,pipe()then()更强大,看起来前者可以做后者可以做的任何事情。使用then()的一个原因可能是它的名称反映了它作为处理相同数据的函数链终止的作用。

但是是否有一个用例需要then()返回Deferred无法完成的原始pipe(),因为它返回了新的{{1} }}吗

3 个答案:

答案 0 :(得分:102)

由于 jQuery 1.8 .then的行为与.pipe相同:

  

弃用通知:从jQuery 1.8开始,不推荐使用deferred.pipe()方法。应该使用取代它的deferred.then()方法。

  

从jQuery 1.8 开始,deferred.then()方法返回一个新的承诺,可以过滤通过函数延迟的状态和值,替换现在已弃用的deferred.pipe()方法

以下示例可能对某些人有所帮助。


它们用于不同的目的:

  • .then()将在您处理过程结果时使用,即文档说明,当解决或拒绝延迟对象时。它与使用.done().fail()

  • 相同
  • 您会以某种方式使用.pipe()(预)过滤结果。回调到.pipe()的返回值将作为参数传递给donefail回调。它还可以返回另一个延迟对象,并且将在此延迟对象上注册以下回调。

    .then()(或.done().fail())不是这种情况,注册回调的返回值只会被忽略。

因此,您使用 .then() .pipe()并非如此。您可以使用.pipe()用于与.then()相同的目的,但反过来却不成立。


示例1

某些操作的结果是一个对象数组:

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

并且您想要计算值的最小值和最大值。让我们假设我们使用两个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调用。有时您希望在前一个Ajax调用完成后启动一个Ajax调用。一种方法是在done回调中进行第二次调用:

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

现在假设你想要解耦你的代码并将这两个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的其他代码为第二个 Ajax调用附加回调,但

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

不会产生预期的效果,因为第二次调用是在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调用,而不会暴露实际的调用流/顺序。


通常,延迟对象提供了一种有趣的方法来解耦代码:)

答案 1 :(得分:6)

在任何情况下,您都必须使用then()而不是pipe()。您始终可以选择忽略pipe()将传入的值。可能在使用pipe时会略微受到影响 - 但这不太重要。

因此,在这两种情况下,您似乎总是可以使用pipe()但是,通过使用pipe(),您正在与其他人阅读您的代码(包括您自己,从现在起六个月后),与回复有一些 important 值。如果你丢弃它,你就违反了这个语义结构。

这就像有一个函数返回一个从未使用过的值:令人困惑。

因此,应该在需要的时候使用then(),在应该的时候使用pipe()

答案 2 :(得分:5)

事实上,事实证明.then().pipe()之间的差异被认为是不必要的,并且它们与jQuery版本1.8相同。

来自jQuery的错误跟踪器a comment by jaubourg中的ticket #11010“使DEFERRED.THEN == DEFERRED.PIPE LIKE PROMISE / A”:

  

在1.8中,我们将删除旧的然后用当前的管道替换它。但非常麻烦的结果是我们必须告诉人们使用非标准的完成,失败和进度,因为提案不提供简单,高效,意味着只需添加一个回调。

(emphassis mine)