什么时候应该使用jQuery deferred的“ then”方法,什么时候应该使用“ pipe”方法?


97

jQuery的Deferred两个功能可用于实现功能的异步链接:

then()

deferred.then( doneCallbacks, failCallbacks ) Returns: Deferred

doneCallbacks解析Deferred时调用的一个函数或函数数组。
failCallbacks拒绝Deferred时调用的一个函数或函数数组。

pipe()

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

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

我知道then()pipe()这要长一些,因此后者必须增加一些额外的好处,但是到底有什么不同是我所不知道的。两者都使用几乎相同的回调参数,尽管它们的名称不同,并且返回a Deferred和返回a 之间的差异Promise似乎很小。

我已经读过一遍又一遍的官方文档,但总是觉得它们太“密集”,以至于无法真正绕开我的脑袋,搜索已经找到了很多关于一个功能或另一个功能的讨论,但是我还没有发现任何可以真正阐明不同之处的东西。各自的优缺点。

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


加成

Felix的出色回答确实有助于阐明这两个功能的不同之处。但我想知道是否有时会比的功能then()更受欢迎pipe()

显然,pipe()它的功能比强大then(),而且似乎前者可以做任何事,后者可以做。使用的一个原因then()可能是其名称反映了其作用,即终止处理相同数据的功能链的作用。

但是,是否有一个用例需要then()require返回由于返回新值而Deferred无法完成的原始操作?pipe()Promise


1
我想了一会儿,但是,我想不到任何用例。如果不需要新的promise对象(我不知道它们在内部如何链接在一起),则可能只是开销。就是说,当然有些人比我更了解这一点。
Felix Kling 2012年

6
对这个问题感兴趣的人肯定会对jQuery Bug跟踪器上的票证#11010感兴趣:MAKE DEFERRED.THEN == DEFERRED.PIPE像
承诺

Answers:


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()将作为参数传递给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
        });
    });
}

您想使用deferred对象来允许其他代码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调用中,而无需暴露调用的实际流程/顺序。


通常,延迟对象提供了一种有趣的方式来分离代码:)


啊,是的,我忽略了pipe可以做的过滤工作then。但是在谷歌搜索这些主题中,似乎他们选择了它pipe而不是filter因为认为过滤是随之而来的额外奖励,而pipe更清楚地表明了其真正目的。因此,似乎除了过滤之外还应该存在其他差异。(再说我承认我真的不明白的过滤功能,即使您的例子应该。result values;return values;的方式?)
hippietrail

当我说我不理解您的示例时,是否是这样的:在上面的示例中,两个.then()接收相同的数据result,您每次都在其中进行过滤;而在下面的示例中,则在传递.pipe()数据result之前将其中的某些数据删除,因为result随后.then()的两个s将接收?
hippietrail 2012年

1
@hippietrail:在此期间,我更新了答案,还包括的其他目的.pipe()。如果回调返回延迟的对象,则将为该对象注册后续的完成或失败回调。我将再举一个例子。编辑:关于您的第二条评论:是的。
菲利克斯·克林

因此,一个区别是数据流经 pipe()then()更像是叶节点,在该叶节点的末尾您必须使用您的数据,并且数据流不会继续下去,并且尽管then()返回的事实实际上Deferred并没有使用/有用?如果这是正确的,则可能有助于澄清/* do something with "min"/"max" */在每个“ .then()子句”中包含类似内容。
hippietrail 2012年

1
不用担心:)我还花了一些时间来完全理解延迟对象及其方法的工作方式。但是一旦您了解了它,它似乎就不再困难了。我同意可以很容易地编写文档。
菲利克斯·克林

7

在任何情况下,您都不能使用then()over pipe()。您始终可以选择忽略pipe()将要传入的值。使用可能会对性能造成轻微影响pipe-但这无关紧要。

因此,似乎您可以pipe()在两种情况下都始终使用。但是,通过使用pipe(),您正在与其他人一起阅读您的代码(包括您自己,距离现在六个月),这表明返回值很重要。如果要丢弃它,就违反了这种语义结构。

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

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


3
我已经在K. Scott Allen的博客“ Experiments In Writing”中找到了一个使用这两个实例的真实示例:Geolocation,Geocoding和jQuery Promises:“然后,控制逻辑的读取效果很好:” $(function () { $.when(getPosition()) .pipe(lookupCountry) .then(displayResults); }); “请注意,管道与然后因为烟斗给了新的希望。”
hippietrail 2012年

5

其实事实证明,之间的差异.then(),并.pipe()已被认为是不必要的,他们已经做成一样的jQuery的版本1.8。

有注释jaubourg的jQuery的bug跟踪票#11010 “MAKE DEFERRED.THEN == DEFERRED.PIPE LIKE PROMISE / A”:

在1.8中,我们将删除旧的,然后将其替换为当前管道。但是,非常令人遗憾的结果是,我们必须告诉人们使用非标准的完成,失败和进步,因为该提议没有提供简单的EFFICIENT意味着仅添加回调。

(Emphassis矿)


1
到目前为止最好的参考,我正在寻找高级用法。
TWiStErRob 2013年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.