98

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

then()

deferred.then( doneCallbacks, failCallbacks ) Returns: Deferred

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

pipe()

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

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

我知道then()已经存在了一点时间,pipe()所以后者必须增加一些额外的好处,但我不知道究竟有什么区别。两者都采用几乎相同的回调参数,尽管它们的名称不同,并且返回 aDeferred和返回 a之间的差异Promise似乎很小。

我一遍又一遍地阅读官方文档,但总是发现它们太“密集”而无法真正包裹我的头,搜索发现很多关于一个或另一个功能的讨论,但我没有找到任何真正澄清不同的东西各有优劣。

那么什么时候用比较好then,什么时候用比较pipe好呢?


添加

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

很明显,它比前者pipe()更强大then(),而且似乎前者可以做后者可以做的任何事情。使用的一个原因then()可能是它的名称反映了它作为处理相同数据的一系列函数的终止角色。

但是是否有一个用例需要返回由于返回新的而无法完成then()的原件?Deferredpipe()Promise

4

3 回答 3

106

由于jQuery 1.8 .then的行为与.pipe

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

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

下面的示例可能仍然对某些人有所帮助。


它们有不同的用途:

  • .then()将在您想要处理过程的结果时使用,即如文档所述,当延迟对象被解决或拒绝时。它与使用.done()or相同.fail()

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

    .then()(或.done(), )情况并非如此,.fail()注册回调的返回值将被忽略。

所以不是你使用 .then() or .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 调用,而不会暴露调用的实际流程/顺序。


一般来说,延迟对象提供了一种有趣的方式来解耦你的代码:)

于 2012-03-07T12:06:54.257 回答
7

没有必须使用then()over 的情况pipe()。您始终可以选择忽略pipe()将传入的值。使用可能会对性能造成轻微影响pipe——但这不太重要。

所以看起来你可以简单地pipe()在这两种情况下都使用。但是,通过使用pipe(),您正在与阅读您的代码的其他人(包括您自己,从现在起六个月后)交流,返回值有一些重要性。如果你丢弃它,你就违反了这个语义结构。

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

所以在你应该使用then()的时候,以及pipe()你应该使用的时候......

于 2012-06-18T19:19:13.970 回答
5

事实上,.then()和之间的区别.pipe()被认为是不必要的,它们与 jQuery 版本 1.8 相同。

来自jQuery 的 bug tracker ticket #11010 “MAKE DEFERRED.THEN == DEFERRED.PIPE LIKE PROMISE/A”中的评论:jaubourg

在 1.8 中,我们将删除旧的 then 并用当前管道替换它。但非常令人痛心的后果是,我们将不得不告诉人们使用非标准的 done、fail 和 progress,因为该提案没有提供简单、高效的方法,即只添加一个回调。

(强调我的)

于 2012-08-07T08:27:04.543 回答