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

hip*_*ail 96 jquery asynchronous decoupling jquery-chaining jquery-deferred

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

then()

deferred.then( doneCallbacks, failCallbacks ) Returns: Deferred

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

pipe()

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

doneFilter解析Deferred时调用的可选函数.
failFilter拒绝Deferred时调用的可选函数.

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

我已经一遍又一遍地阅读官方文档,但总是发现它们太"密集"而无法真正地包围我的搜索已经找到了很多关于这个或另一个特征的讨论,但我没有发现任何真正澄清不同的东西每个人的利弊.

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


加成

Felix的出色答案确实有助于阐明这两个功能的区别.但我想知道是否有时候功能性then()比那更好pipe().

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

但是有一个用例需要then()返回由于它返回一个新的而Deferred无法完成的原始文件吗?pipe()Promise

Fel*_*ing 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}]
Run Code Online (Sandbox Code Playgroud)

并且您想要计算值的最小值和最大值.让我们假设我们使用两个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" */ 

});
Run Code Online (Sandbox Code Playgroud)

在这两种情况下,您都必须遍历列表并从每个对象中提取值.

以某种方式预先提取值是不是更好,这样你就不必单独在两个回调中执行此操作?是! 这就是我们可以.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" */

});
Run Code Online (Sandbox Code Playgroud)

显然这是一个组成的例子,有许多不同的(可能更好的)方法来解决这个问题,但我希望它说明了这一点.


例2

考虑Ajax调用.有时您希望在上一个Ajax调用完成后启动一个Ajax调用.一种方法是在done回调中进行第二次调用:

$.ajax(...).done(function() {
    // executed after first Ajax
    $.ajax(...).done(function() {
        // executed after second call
    });
});
Run Code Online (Sandbox Code Playgroud)

现在让我们假设您想要解耦代码并将这两个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
        });
    });
}
Run Code Online (Sandbox Code Playgroud)

您希望使用延迟对象来允许其他代码调用makeCalls第二次 Ajax调用附加回调,但是

makeCalls().done(function() {
    // this is executed after the first Ajax call
});
Run Code Online (Sandbox Code Playgroud)

因为第二次调用是在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
});
Run Code Online (Sandbox Code Playgroud)

通过使用,.pipe()您现在可以将回调附加到"内部"Ajax调用,而不会暴露实际的调用流/顺序.


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


Ale*_*man 7

没有你必须使用的then()情况pipe().你总是可以选择忽略pipe()传入的值.使用时可能会有轻微的性能损失pipe- 但这不太重要.

所以看起来你可能只是总是pipe()在两种情况下使用.但是,通过使用pipe(),您正在与读取您的代码的其他人(包括您自己,六个月后)进行沟通,以确定返回值的重要性.如果你丢弃它,你就违反了这个语义结构.

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

所以then()在你需要的时候使用,什么pipe()时候应该......

  • 我在K. Scott Allen的博客"写作中的实验"中找到了一个真实的例子:**[Geolocation,Geocoding和jQuery Promises](http://odetocode.com/Blogs/scott/archive /2012/06/14/geolocation-geocoding-and-jquery-promises.aspx)**:"然后控制逻辑读得很好:"`$(function(){$ .when(getPosition()).pipe( lookupCountry).then(displayResults);});`"注意管道不同于管道,因为管道会返回一个新的诺言." (3认同)

hip*_*ail 5

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

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

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

(emphassis mine)