我一直在阅读有关jQuery延迟和承诺的信息,但看不到使用.then()
&.done()
进行成功回调之间的区别。我知道Eric Hynds提到.done()
并.success()
映射到相同的功能,但是我猜是这样,.then()
因为所有的回调都在成功完成操作后被调用。
有人可以启发我正确使用吗?
我一直在阅读有关jQuery延迟和承诺的信息,但看不到使用.then()
&.done()
进行成功回调之间的区别。我知道Eric Hynds提到.done()
并.success()
映射到相同的功能,但是我猜是这样,.then()
因为所有的回调都在成功完成操作后被调用。
有人可以启发我正确使用吗?
Answers:
done()
延迟解决后,将触发附加到的回调。附加到的回调fail()
当延迟被拒绝时将触发。
在jQuery 1.8之前,then()
只是语法糖:
promise.then( doneCallback, failCallback )
// was equivalent to
promise.done( doneCallback ).fail( failCallback )
从1.8版本开始,then()
是的别名pipe()
并返回新的Promise,有关的更多信息,请参见此处pipe()
。
success()
并且error()
仅在jqXHR
调用返回的对象上可用ajax()
。他们是简单的别名done()
和fail()
分别为:
jqXHR.done === jqXHR.success
jqXHR.fail === jqXHR.error
此外,done()
它不仅限于单个回调,而且会过滤掉非功能(尽管1.8版中存在字符串错误,应在1.8.1中修复):
// 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()
。
then
兑现新的承诺是我缺少的关键。我不明白为什么像这样的链$.get(....).done(function(data1) { return $.get(...) }).done(function(data2) { ... })
因data2
未定义而失败;当我改用它时done
,then
它是有效的,因为我真的很想将诺言发送到一起,而不是将更多的处理程序附加到原始诺言上。
done
还是then
?为什么?
返回结果的处理方式也有所不同(称为链接,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
})
done
不then
更改结果的结果。其他imo错过了巨大的机会。
then
在1.8 中已更改
done
示例相同。更改then
为pipe
1.8之前的版本即可获得1.8+的then
行为。
.done()
只有一个回调,这是成功的回调
.then()
具有成功和失败回调
.fail()
只有一个失败回调
因此,由您决定您必须做什么...是否在乎它是否成功?
then()
十分不同done()
。正如then()
通常在成功回调中通常所说的那样,您的意思是一个细节,而不是要记住/知道的主要内容。(不能说jQuery 3.0之前的情况。)
添加仅在Deferred解决后才调用的处理程序。您可以添加多个要调用的回调。
var url = 'http://jsonplaceholder.typicode.com/posts/1';
$.ajax(url).done(doneCallback);
function doneCallback(result) {
console.log('Result 1 ' + result);
}
您也可以像上面这样写
function ajaxCall() {
var url = 'http://jsonplaceholder.typicode.com/posts/1';
return $.ajax(url);
}
$.when(ajaxCall()).then(doneCallback, failCallback);
添加要解析,拒绝或仍在进行中的Deferred时要调用的处理程序。
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);
}
then
如果没有fail
提供回调,您的帖子将不清楚行为-即根本没有捕捉到这种fail
情况
实际上,就jQuery的Deferreds而言,它是Promises的实现(而jQuery3.0实际上试图将它们纳入规范),实际上存在一个非常关键的区别。
完成/然后之间的主要区别是
.done()
无论您做什么或返回什么,总是返回与开始时相同的Promise / wrapped值。 .then()
总是返回一个新的Promise,并且您负责根据传递给它的函数控制Promise的内容。 从jQuery转换为本地ES2015 Promises,.done()
有点像在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));
这将记录:
5
未定义
发生了什么?当我使用.then并将其传递给一个不返回任何内容的函数时,它的隐式结果是“未定义” ...当然,该结果将Promise [undefined]返回给下一个then方法,该方法记录了未定义。因此,我们开始时的原始价值基本上已经丢失。
.then()
本质上是函数组合的一种形式:每个步骤的结果都用作下一步中该函数的参数。这就是为什么最好将.done认为是“敲击”的原因->它实际上不是组合的一部分,而只是在某个步骤中偷偷看了一下值并以该值运行功能,但实际上并没有改变以任何方式组成。
这是一个非常根本的差异,并且很可能有一个很好的理由,为什么本机Promises本身没有实现.done方法。我们不必深入探讨为什么没有.fail方法,因为这更加复杂(即,.fail / .catch不是.done / .then的镜像。.->。catch中返回裸值的函数不会像传递给.the的那些人一样拒绝了“ stay”,然后他们解决了!)
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()
write result = 7
。第二个then()
取值为7,a
而取值undefined
为b
,因此第二个then()
返回参数为NaN的新诺言,最后一个done()
输出NaN作为结果。
jQuery.Deferred()
可以接收多个值,并将其正确地传递到第一个值.then()
。尽管有点奇怪,但是任何后续代码.then()
都不能这样做。(通过选择的接口return
只能返回一个值。)Javascript的本机Promise
不执行该操作。(老实说,这是更一致的。)
.then()
这些是缺点 .done()
resolve()
调用(所有.done()
处理程序将同步执行)resolve()
可能会从注册中获得例外 .done()
处理程序中(!).done()
半数导致延迟:
.done()
处理程序将被默默地跳过我暂时认为这.then(oneArgOnly)
始终需要.catch()
确保没有异常被静默忽略,但这不再是真的:该unhandledrejection
事件.then()
在控制台上记录未处理的异常(默认情况下)。很合理!没有理由使用.done()
。
以下代码段揭示了这一点:
.done()
处理程序将在resolve()
.done()
影响resolve()
者中的
例外resolve()
.done()
解决的
希望.then()
没有这些问题
unhandledrejection
似乎没有)顺便说一句,.done()
无法正确捕获来自的异常:由于的同步模式.done()
,错误要么在.resolve()
(可能是库代码!)的点抛出,要么在.done()
已经解决了延期问题的情况下附在罪魁祸首的调用上。
console.log('Start of script.');
let deferred = $.Deferred();
// deferred.resolve('Redemption.');
deferred.fail(() => console.log('fail()'));
deferred.catch(()=> console.log('catch()'));
deferred.done(() => console.log('1-done()'));
deferred.then(() => console.log('2-then()'));
deferred.done(() => console.log('3-done()'));
deferred.then(() =>{console.log('4-then()-throw');
throw 'thrown from 4-then()';});
deferred.done(() => console.log('5-done()'));
deferred.then(() => console.log('6-then()'));
deferred.done(() =>{console.log('7-done()-throw');
throw 'thrown from 7-done()';});
deferred.done(() => console.log('8-done()'));
deferred.then(() => console.log('9-then()'));
console.log('Resolving.');
try {
deferred.resolve('Solution.');
} catch(e) {
console.log(`Caught exception from handler
in resolve():`, e);
}
deferred.done(() => console.log('10-done()'));
deferred.then(() => console.log('11-then()'));
console.log('End of script.');
<script
src="https://code.jquery.com/jquery-3.4.1.min.js"
integrity="sha384-vk5WoKIaW/vJyUAd9n/wmopsmNhiy+L2Z+SBxGYnUkunIxVxAv/UtMOhba/xskxh"
crossorigin="anonymous"
></script>
done
如果先前完成的操作有异常,则不会执行。但是为什么会默默地忽略它,我的意思是发生了异常,所以为什么您说它是默默的。2)我鄙视该Deferred
对象,因为它的API做得非常差。这太复杂和令人困惑。您的代码在这里既无助于证明您的观点,也为您要证明的内容带来了不必要的复杂性。3)为什么done
在第二个索引之前执行at索引2、4和6 then
?
.then()
无论是否引发异常(在这些处理程序中),都将被调用。但是增加/剩余.done()
中断。
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>
这将输出:
then
now
现在,取代done()
通过then()
以非常相似的代码片段:
var d = $.Deferred();
d.then(() => console.log('then'));
d.resolve();
console.log('now');
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.0.0/jquery.min.js"></script>
现在的输出是:
now
then
因此,对于立即解决的延迟,传递给函数的done()
总是以同步方式调用,而传递给参数的then()
则是异步调用。
这与先前的jQuery版本不同,在这两个版本中,两个回调均被同步调用,如升级指南中所述:
遵守Promises / A +要求的另一个行为更改是Deferred .then()回调始终被异步调用。以前,如果将.then()回调添加到已解决或拒绝的Deferred中,则该回调将立即并同步运行。
.done()
终止承诺链,确保没有其他事情可以采取进一步措施。这意味着jQuery promise实现可以抛出任何未处理的异常,因为没有人可以使用来处理它.fail()
。
实际上,如果您不打算在承诺中附加更多步骤,则应使用.done()
。有关更多详细信息,请参见为什么需要兑现承诺
.done()
没有终结作用的jQuery,则不是。该文档说:“由于deferred.done()返回延迟的对象,因此可以将延迟对象的其他方法链接到该对象,包括其他.done()方法。” .fail()
没有提及,但是,可以,也可以将其链接起来。