.then()
和 .done()
之间的区别。我知道Eric Hynds提到.done()
和.success()
映射到相同的功能,但我猜.then()
也是如此,因为所有回调都在成功操作完成后被调用。请问有人可以启示我正确的用法吗?
.then()
和 .done()
之间的区别。我知道Eric Hynds提到.done()
和.success()
映射到相同的功能,但我猜.then()
也是如此,因为所有回调都在成功操作完成后被调用。done()
附加的回调函数将在deferred对象被解决时触发。fail()
附加的回调函数将在deferred对象被拒绝时触发。
在jQuery 1.8之前,then()
仅仅是一种语法糖:
promise.then( doneCallback, failCallback )
// was equivalent to
promise.done( doneCallback ).fail( failCallback )
自1.8版本起,then()
是pipe()
的别名,返回一个新的Promise对象。关于pipe()
更多信息请参见这里。
success()
和error()
仅适用于调用ajax()
方法返回的jqXHR
对象。它们分别是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()
同样适用。
$.get(....).done(function(data1) { return $.get(...) }).done(function(data2) { ... })
这样的链式调用会导致data2
未定义;当我将done
改为then
时,它就起作用了,因为我实际上想要将Promise对象串联在一起,而不是将更多的处理程序附加到原始Promise对象上。 - wrschneider在处理返回结果的方式上也有所不同(这被称为链式调用,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 将等待内部 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
})
// 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
})
then
的行为在1.8版本中发生了变化。 - bradley.ayers.done()
只有一个回调函数,即成功回调函数。
.then()
有成功和失败两个回调函数。
.fail()
只有一个失败回调函数。
所以你需要决定必须做什么...你在意它成功还是失败?
then()
与done()
非常不同。由于then()
通常仅使用成功回调函数进行调用,因此你的观点只是一个细节,而不是要记住/知道的主要内容。(无法说在jQuery 3.0之前是怎样的。) - Robert Siemer向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);
添加回调函数,用于在延迟对象已解决、被拒绝或仍在进行中时被调用。
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);
}
fail
回调函数,then
会如何行为 - 即根本不捕获fail
情况。 - B M实际上,jQuery的Deferred(延迟对象)与Promises有一个相当重要的区别(而且jQuery 3.0试图将它们带入规范之中)。
done/then之间的关键区别在于:
.done()
无论你做什么或返回什么,它始终返回与它开始时相同的Promise/wrapped值。.then()
始终返回一个新的Promise,您可以通过控制传递给它的函数返回值来控制该Promise。从jQuery翻译成本地ES2015 Promises后,.done()
在某种程度上像是在Promise链中的函数周围实现了一个“tap”结构,因为如果链处于“resolve”状态,它将把一个值传递给函数…但该函数的结果不会影响链本身。
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
undefined
发生了什么?当我使用.then并传递一个没有返回任何内容的函数时,它的隐含结果是"undefined"......这当然会返回一个 Promise[undefined] 给下一个 then 方法,然后记录 undefined。因此,我们最初启动的值基本上丢失了。
.then()
本质上是一种函数组合形式:每个步骤的结果被用作下一步中函数的参数。这就是为什么 .done 最好被认为是一个“tap”-> 它实际上不是组合的一部分,而只是在特定步骤查看值并运行函数的一种方式,但实际上并不改变组合的方式。
这是一个非常基本的区别,原生的 Promises 没有 .done 方法实现自己可能有很好的理由。我们甚至不必进入为什么没有 .fail 方法,因为那更加复杂(即,在 .catch 中返回裸值的函数不像那些传递给 .then 的函数那样“保持”拒绝状态,它们被解析!)
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 );
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作为其结果。
jQuery.Deferred()
可以接收多个值,并将其正确传递给第一个 .then()
。但有点奇怪...因为任何后续的 .then()
都不能这样做。(通过 return
选择的接口只能返回一个值。)JavaScript 的原生 Promise
并不会这样做。(说实话更加一致。) - Robert Siemer.then()
.done()
没有任何优势,而且存在以下缺点:
a.done().done()
等同于a.done(); a.done()
,可以用a.then(); a.then()
代替.done()
无法实现a.then().then()
resolve()
调用(所有.done()
处理程序将同步执行)resolve()
可能会受到已注册的.done()
处理程序异常的影响!.done()
中的异常会使deferred出现问题:
.done()
处理程序将被静默跳过.then()
没有这些问题我曾暂时认为.then(oneArgOnly)
总是需要.catch()
,以便不会忽略任何异常,但现在不再是这样:默认情况下,unhandledrejection
事件会在控制台上记录未处理的.then()
异常。非常合理!完全没有理由继续使用.done()
。
以下代码片段揭示了:
.done()
处理程序将在resolve()
点同步调用
.done()
中的异常会影响resolve()
调用者
resolve()
的catch进行记录.done()
中的异常会破坏promise继续被.done()
解决
.then()
没有这些问题
unhandledrejection
)顺便说一下,.done()
中的异常无法被正确捕获:由于.done()
的同步模式,错误要么在.resolve()
点抛出(可能是库代码!),要么在已解决deferred上的.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) 为什么在第二个 then
之前执行索引为2、4和6的 done
? - CodingYoshi.then()
都会被调用,无论是否引发异常(在这些处理程序中)。但是附加/剩余的 .done()
却会中断。 - Robert Siemer从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()
的任何参数都是异步调用。Promises/A+兼容性所需的另一个行为更改是Deferred.then()回调始终以异步方式调用。 之前,如果将.then()回调添加到已经解析或拒绝的Deferred中,回调将立即且同步运行。
then()
。我的测试失败是因为回调是异步调用的,在测试结束后才被调用。使用 done()
,回调被同步调用,满足测试期望,测试通过了。 - Shant Dashjian$.getJSON( 'dataservice/General', {action:'getSessionUser'} )
.then( function( user ) {
console.log( user );
return $.getJSON( 'dataservice/Address', {action:'getFirstAddress'} );
})
.then( function( address ) {
console.log( address );
})
在这里,第二个.then跟随返回的$.getJSON。