关于jQuery延迟和承诺:jQuery延迟和承诺 – .then()vs .done()

jQuery deferreds and promises - .then() vs .done()

我一直在阅读jquery延迟和承诺,我看不到使用.then().done()成功回调之间的区别。我知道Eric Hynds提到.done().success()映射到相同的功能,但我猜.then()也是如此,因为所有回调都是在成功操作完成后调用的。

有人能告诉我正确的用法吗?


当延迟解决时,将触发附加到done()的回调。当延迟被拒绝时,附加到fail()的回调将被激发。

在jquery 1.8之前,then()只是语法上的糖分:

1
2
3
promise.then( doneCallback, failCallback )
// was equivalent to
promise.done( doneCallback ).fail( failCallback )

截至1.8,then()pipe()的别名,并返回新的承诺,有关pipe()的更多信息,请参阅此处。

success()error()仅在通过调用ajax()返回的jqXHR对象上可用。它们分别是done()fail()的简单别名:

1
2
jqXHR.done === jqXHR.success
jqXHR.fail === jqXHR.error

另外,done()不仅限于单个回调,而且会过滤掉非函数(尽管在1.8.1版本中有一个字符串错误,应该在1.8.1中修复):

1
2
3
// this will add fn1 to 7 to the deferred's internal callback list
// (true, 56 and"omg" will be ignored)
promise.done( fn1, fn2, true, [ fn3, [ fn4, 56, fn5 ],"omg", fn6 ], fn7 );

同样适用于fail()


在处理返回结果的方式上也存在差异(称为链接,done不链接,then生成调用链)

1
2
3
4
5
6
7
8
promise.then(function (x) { // Suppose promise returns"abc"
    console.log(x);
    return 123;
}).then(function (x){
    console.log(x);
}).then(function (x){
    console.log(x)
})

将记录以下结果:

1
2
3
abc
123
undefined

同时

1
2
3
4
5
6
7
8
promise.done(function (x) { // Suppose promise returns"abc"
    console.log(x);
    return 123;
}).done(function (x){
    console.log(x);
}).done(function (x){
    console.log(x)
})

将得到以下信息:

1
2
3
abc
abc
abc

-------更新:

顺便说一句,我忘了说,如果您返回一个承诺而不是原子类型值,那么外部承诺将一直等到内部承诺解决:

1
2
3
4
5
6
7
8
9
10
11
promise.then(function (x) { // Suppose promise returns"abc"
    console.log(x);
    return $http.get('/some/data').then(function (result) {
        console.log(result); // suppose result ==="xyz"
        return result;
    });
}).then(function (result){
    console.log(result); // result === xyz
}).then(function (und){
    console.log(und) // und === undefined, because of absence of return statement in above then
})

这样,组成并行或顺序异步操作变得非常简单,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
// Parallel http requests
promise.then(function (x) { // Suppose promise returns"abc"
    console.log(x);

    var promise1 = $http.get('/some/data?value=xyz').then(function (result) {
        console.log(result); // suppose result ==="xyz"
        return result;
    });

    var promise2 = $http.get('/some/data?value=uvm').then(function (result) {
        console.log(result); // suppose result ==="uvm"
        return result;
    });

    return promise1.then(function (result1) {
        return promise2.then(function (result2) {
           return { result1: result1, result2: result2; }
        });
    });
}).then(function (result){
    console.log(result); // result === { result1: 'xyz', result2: 'uvm' }
}).then(function (und){
    console.log(und) // und === undefined, because of absence of return statement in above then
})

上面的代码并行地发出两个HTTP请求,从而使请求更快完成,而下面的那些HTTP请求则按顺序运行,从而减少服务器负载。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
// Sequential http requests
promise.then(function (x) { // Suppose promise returns"abc"
    console.log(x);

    return $http.get('/some/data?value=xyz').then(function (result1) {
        console.log(result1); // suppose result1 ==="xyz"
        return $http.get('/some/data?value=uvm').then(function (result2) {
            console.log(result2); // suppose result2 ==="uvm"
            return { result1: result1, result2: result2; };
        });
    });
}).then(function (result){
    console.log(result); // result === { result1: 'xyz', result2: 'uvm' }
}).then(function (und){
    console.log(und) // und === undefined, because of absence of return statement in above then
})


.done()只有一个回调,是成功回调

.then()既有成功回调,也有失败回调。

.fail()只有一个失败回调

所以你必须做什么…你在乎它是成功还是失败?


延迟。完成()

添加仅在解决延迟时调用的处理程序。可以添加多个要调用的回调。

1
2
3
4
5
6
var url = 'http://jsonplaceholder.typicode.com/posts/1';
$.ajax(url).done(doneCallback);

function doneCallback(result) {
    console.log('Result 1 ' + result);
}

你也可以这样写,

1
2
3
4
5
6
function ajaxCall() {
    var url = 'http://jsonplaceholder.typicode.com/posts/1';
    return $.ajax(url);
}

$.when(ajaxCall()).then(doneCallback, failCallback);

号延迟。然后())

添加在解决、拒绝或仍在进行延迟时要调用的处理程序。

1
2
3
4
5
6
7
8
9
10
var url = 'http://jsonplaceholder.typicode.com/posts/1';
$.ajax(url).then(doneCallback, failCallback);

function doneCallback(result) {
    console.log('Result ' + result);
}

function failCallback(result) {
    console.log('Result ' + result);
}


实际上有一个非常关键的区别,因为jquery的延迟意味着承诺的实现(而jquery3.0实际上试图将它们引入规范)。

"完成/然后"之间的关键区别是

  • 无论您做什么或返回什么,.done()始终返回与它开始时相同的承诺/包装值。
  • .then()总是返回一个新的承诺,而您负责根据您传递给它的函数来控制该承诺的内容。

从jquery转换为原生的es2015 promises,.done()有点像在promise链中的函数周围实现一个"tap"结构,如果链处于"resolve"状态,它将向函数传递一个值…但是这个函数的结果不会影响链本身。

1
2
3
4
5
6
7
8
9
const doneWrap = fn => x => { fn(x); return x };

Promise.resolve(5)
       .then(doneWrap( x => x + 1))
       .then(doneWrap(console.log.bind(console)));

$.Deferred().resolve(5)
            .done(x => x + 1)
            .done(console.log.bind(console));

这两个记录都是5,而不是6。

注意,我用done和donewrap来做日志记录,而不是。这是因为console.log函数实际上不返回任何内容。如果你通过了,会发生什么呢,那么一个不返回任何内容的函数?

1
2
3
4
Promise.resolve(5)
       .then(doneWrap( x => x + 1))
       .then(console.log.bind(console))
       .then(console.log.bind(console));

这将记录:

5

undefined

发生什么事了?当我使用时。然后传递给它一个不返回任何内容的函数,它的隐式结果是"未定义"…当然,它向下一个then方法返回了一个promise[未定义],记录了未定义的。所以我们开始时的原值基本上是丢失的。

.then()本质上是一种函数组合形式:每一步的结果都被用作下一步函数的参数。这就是原因。完成最好被认为是一个"tap"->它实际上不是组成部分,只是在某个步骤中偷看值并以该值运行函数,但实际上不会以任何方式改变组成部分。

这是一个非常基本的区别,而且可能有一个很好的理由,为什么本地的承诺没有一个.done方法实现自己。我们不必解释为什么没有.fail方法,因为这更复杂(即,.fail/.catch不是.done/.then->functions in.catch中返回裸值的镜像,而不是像传递给的那样"保留"被拒绝。然后,它们就解决了!)


then()总是意味着无论在什么情况下都将调用它。但是在不同的jquery版本中传递的参数不同。

在jquery 1.8之前,then()等于done().fail()。所有回调函数共享相同的参数。

但从jquery 1.8开始,then()返回一个新的承诺,如果它有返回值,它将被传递到下一个回调函数。

让我们看看下面的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var defer = jQuery.Deferred();

defer.done(function(a, b){
            return a + b;
}).done(function( result ) {
            console.log("result =" + result);
}).then(function( a, b ) {
            return a + b;
}).done(function( result ) {
            console.log("result =" + result);
}).then(function( a, b ) {
            return a + b;
}).done(function( result ) {
            console.log("result =" + result);
});

defer.resolve( 3, 4 );

在jquery 1.8之前,答案应该是

1
2
3
result = 3
result = 3
result = 3

所有result取3。而then()函数总是将相同的延迟对象传递给下一个函数。

但从jquery 1.8开始,结果应该是:

1
2
3
result = 3
result = 7
result = NaN

因为第一个then()函数返回一个新的promise,而值7(这是唯一将传递的参数)被传递给下一个done(),所以第二个done()函数编写result = 7。第二个then()以7为a的值,以undefinedb的值,因此第二个then()返回一个参数为nan的新承诺,最后一个done()打印nan。


在其他答案中很难找到一个非常简单的心理作图反应:

  • done执行tap就像蓝鸟的承诺一样。

  • then按照es6承诺执行then


.done()终止了承诺链,确保没有任何其他附加步骤。这意味着jQueryPromise实现可以抛出任何未处理的异常,因为没有人可以使用.fail()来处理它。

实际上,如果你不打算在一个承诺上附加更多的步骤,你应该使用.done()。欲了解更多详情,请参阅为何需要履行承诺。