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

有人可以启发我正确使用吗?

评论

请注意所有人,2016年6月发布的JQuery 3.0是第一个符合Promises / A +和ES2015 Promises规范的版本。在此之前的实现与应有的承诺不兼容。

我更新了答案,并提出了有关何时使用的改进建议。

#1 楼

解决延迟问题后,将触发done()附带的回调。

在jQuery 1.8之前,fail()只是语法糖:

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


从1.8版本开始,then()then()的别名并返回新的Promise,请参见此处以获取有关pipe()的更多信息。它们分别是pipe()success()的简单别名:带有应在1.8.1中修复的版本1.8中的字符串:

评论


然后重新履行诺言是我缺少的关键。我不明白为什么像$ .get(....)。done(function(data1){return $ .get(...)})。done(function(data2){...})这样的链由于未定义data2而失败;当我将完成更改为后来起作用时,因为我真的很想将promise放在一起,而不是将更多的处理程序附加到原始promise。

–wrschneider
15年3月27日在14:46

jQuery 3.0是第一个符合Promises / A +和ES2015规范的版本。

– Flimm
16年8月19日在16:58

我仍然不明白为什么我要在另一个上使用一个。如果我进行了ajax调用,而我需要等到该调用完全完成(这意味着从服务器返回响应)之后再调用另一个ajax调用,我应该使用done还是then?为什么?

–CodingYoshi
18/12/19在1:51

@CodingYoshi签出我的答案以最终回答该问题(使用.then())。

– Robert Siemer
4月1日7:04

#2 楼

返回结果的处理方式也不同(称为链接,done不链接,而then生成调用链)

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)
})


以下结果将被记录:

abc
123
undefined


,而

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)
})


将得到以下内容:

abc
abc
abc


----------更新:

顺便说一句。我忘了提一下,如果您返回Promise而不是原子类型值,则外部诺言将等到内部诺言得到解决:

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
})


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

// 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请求正在按顺序运行,从而减少服务器负载

// 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
})


评论


+1表示已完成的操作不会对结果产生任何影响,然后更改结果。其他imo错过了巨大的机会。

–假名
13年5月14日在17:54

可能值得一提的是,它适用于什么版本的jQuery,因为then的行为在1.8中已更改

–bradley.ayers
13年5月30日在0:15

+1直达目标。如果有人想查看混合完成的链然后调用结果,我创建了一个可运行的示例。

– Michael Kropat
2014年2月4日在20:34

上面的示例还强调了“完成”对最初创建的原始承诺对象有效,但“然后”返回了新的承诺。

–普拉克·坎蒂·巴塔奇亚里(Pulak Kanti Bhattacharyya)
2014年8月2日在5:23

这适用于jQuery 1.8+。旧版本的行为就像完成的示例一样。然后更改为以1.8之前的版本进行管道传输,以获取1.8+的行为。

– David Harkness
14年8月14日,0:56

#3 楼

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

.then()只有成功和失败回调,

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

所以它由您决定,您必须做什么...是否在乎它是否成功?

评论


您没有提及“然后”产生呼叫链。参见Lu4的答案。

–寡核苷酸
13-10-30在22:02

您的答案是从2011年开始...如今,它们的返回值使then()与done()非常不同。由于then()通常仅在成功回调中被调用,因此您的观点是一个细节而不是要记住/知道的主要事情。 (无法说出jQuery 3.0之前的情况。)

– Robert Siemer
3月31日15:22



#4 楼

deferred.done()

添加仅在解析Deferred后才调用的处理程序。您可以添加多个要调用的回调。 > deferred.then()

添加要解析,拒绝或仍在进行Deferred时调用的处理程序。

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

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


评论


您的帖子不清楚如果没有提供失败回调,那么行为将如何-即根本没有捕获失败案例

– B M
17年12月8日在16:16

失败案例会引发程序顶层可能捕获的异常。您还可以在JavaScript控制台中看到异常。

–David Spector
19年1月20日在21:07

#5 楼

实际上,有一个非常关键的区别,就jQuery的Deferreds而言,它是Promises的实现(而jQuery3.0实际上是试图将它们纳入规范)。
done / then之间的主要区别在于, >

.done()始终返回与开始时相同的Promise /包装值,无论您做什么或返回什么。

.then()从jQuery转换为原生ES2015 Promises,负责控制Promise的功能。承诺链,如果该链处于“解决”状态,它将向函数传递值...但是该函数的结果不会影响链本身。
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进行日志记录,而不是.then。那是因为console.log函数实际上不返回任何东西。如果您传递.then函数不返回任何内容会发生什么呢?
Promise.resolve(5)
       .then(doneWrap( x => x + 1))
       .then(console.log.bind(console))
       .then(console.log.bind(console));

将记录: />发生了什么事?当我使用.then并将其传递给任何不返回任何函数的函数时,它的隐式结果是“未定义” ...当然,该结果当然将Promise [undefined]返回给下一个then方法,该方法记录了未定义。因此,我们开始时使用的原始值基本上已经丢失。这就是为什么.done最好被认为是“敲击”的原因->它实际上不是组成部分,只是在某个步骤中偷偷看值并在该值上运行函数的东西,但实际上并没有改变以任何方式组成。
这是一个非常根本的差异,并且很可能有一个很好的理由,为什么本机Promises本身没有实现.done方法。我们无需深入探讨为什么没有.fail方法,因为这更加复杂(即,.fail / .catch不是.done / .then的镜像。.->。catch中返回裸值的函数不会像传递给.the的那些人一样拒绝了“ stay”,然后他们解决了!)

#6 楼

then()始终表示在任何情况下都将调用它。但是传递的参数在不同的jQuery版本中是不同的。

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

但是从jQuery 1.8开始,then()返回一个新的promise,并且如果它返回一个值,它将被传递到下一个回调函数中。示例:

 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之前,答案应该是

 result = 3
result = 3
result = 3
 





所有result都需要3。并且then()函数始终将相同的延迟对象传递给下一个函数。

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

 result = 3
result = 7
result = NaN
 



因为第一个then()函数返回一个新的promise,所以将值7(这是将传递的唯一参数)传递给下一个done(),因此第二个done()写入result = 7。第二个then()将7用作a的值,并将undefined用作b的值,因此第二个then()返回带有参数NaN的新promise,最后一个done()打印NaN作为其结果。

评论


“ then()始终意味着在任何情况下都将调用它” –不正确。如果Promise内部发生错误,则永远不会调用then()。

–David Spector
19年1月20日在21:08

有趣的方面是jQuery.Deferred()可以接收多个值,并将其正确地传递到第一个.then()。—有点奇怪,但是任何随后的.then()都不能这样做。 (通过return选择的接口只能返回一个值。)JavaScript的本机Promise不会这样做。 (老实说,这是更一致的。)

– Robert Siemer
4月1日1:52

#7 楼

响应中有一个非常简单的思维导图,很难在其他答案中找到:承诺


#8 楼

jQuery 3.0还有一个重要的区别,它很容易导致意外行为,并且在以前的答案中没有提及:

 let d = $.Deferred();
d.done(() => console.log('then'));
d.resolve();
console.log('now'); 

 <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.0.0/jquery.min.js"></script> 





这将输出: br />


 done() 

 then() 





输出现在是:同步方式,而传递给var d = $.Deferred(); d.then(() => console.log('then')); d.resolve(); console.log('now');的任何参数均异步调用。

这与升级版本中提到的同步调用两个回调的jQuery早期版本不同:
Deferred .then()回调始终被异步调用。
以前,如果将.then()回调添加到已经被解析或拒绝的Deferred中,则该回调将立即运行和
同步。


评论


谢谢。这个答案解释了我所看到的行为。我正在使用then()。我的测试失败了,因为测试结束后,回调被称为异步。使用done()回调被同步调用,满足测试期望,测试通过。

–尚达什坚
6月18日22:25

#9 楼

仅使用.then()


这些是.done()的缺点


不能链接
阻止resolve()调用(所有.done()处理程序将

resolve()可能会从已注册的.done()处理程序中获取异常(!)
.done()中的异常会杀死延迟的内容:
我进一步认为,.done()处理程序将被静默跳过



我暂时认为.then(oneArgOnly)始终需要.catch(),因此不会有任何异常被静默忽略,但这不再是真的:unhandledrejection事件在控制台上记录未处理的.then()异常(默认情况下)。很合理!完全没有理由完全使用.done()。在.done()点被称为同步


记录为1、3、5、7
在脚本掉入底部之前记录


> resolve()中的异常会影响.done()调用方


通过捕获resolve()捕获




例外会破坏进一步的resolve()分辨率的承诺
/>

8和10未记录!



.done()没有这些问题


在线程变为空闲后记录为2、4、6、9、11不能正确捕获:由于.then()的同步模式,错误要么在unhandledrejection的点(可能是库代码!)抛出,要么在.done()调用中抛出,如果延期已经解决。




 .done() 

 .resolve() 




评论


有几件事情:1)我明白您在说什么,如果先前的完成有异常,则不会执行完成。但是为什么会默默地忽略它,我的意思是发生了异常,所以为什么您说它是默默的。 2)我鄙视Deferred对象,因为它的API做得非常非常差。这太复杂和令人困惑。您的代码在这里既无助于证明您的观点,又对您要证明的内容具有不必要的复杂性。 3)为什么在索引2,索引4,索引6和索引6之前完成索引2?

–CodingYoshi
4月11日14:04



不好意思,您绝对应该投票。至于您对异常的评论,通常就是异常的工作方式:一旦引发,它之后的代码将不被执行。加上jQuery文档指出,只有在延迟解决后,它才会执行。

–CodingYoshi
4月13日下午1:00

@CodingYoshi这里的情况有所不同:我只是在谈论已解决的承诺/延期。我并不是在抱怨没有成功处理程序的其余部分,这很正常。但是我认为没有理由不称呼一个完全不同的成功承诺者。所有.then()都会被调用,无论是否引发异常(在那些处理程序中)。但是添加/剩余的.done()会中断。

– Robert Siemer
4月13日下午5:48

@CodingYoshi,如果可以说的话,我的回答大大改善了。代码和文字。

– Robert Siemer
4月13日13:00

#10 楼

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

实际上,如果您不打算对Promise附加更多步骤,则应该使用.done()。有关更多详细信息,请参见为什么需要兑现承诺

评论


警告!这个答案对于几种promise实现是正确的,但对于jQuery.done()没有终止作用的jQuery而言,它不是正确的。该文档说:“由于deferred.done()返回延迟的对象,因此可以将延迟对象的其他方法链接到该对象,包括其他.done()方法。”没有提到.fail(),但是,可以,也可以将其链接起来。

–漫游者1888
2014年10月30日16:23

我不好,没有检查jQuery

–gleb bahmutov
2014年10月30日在16:42

@glebbahmutov-也许您应该删除此答案,以免其他人感到困惑?只是一个友好的建议:)

–安德烈
2015年10月13日,0:57

请不要删除答案,这也可以帮助人们消除误解。

–梅利莎
2015年12月21日,下午3:17