jQuery 延迟和承诺 - .then() 与 .done()
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5436327/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
jQuery deferreds and promises - .then() vs .done()
提问by screenm0nkey
I've been reading about jQuery deferreds and promises and I can't see the difference between using .then()
& .done()
for successful callbacks. I know Eric Hyndsmentions that .done()
and .success()
map to the same functionality but I'm guessing so does .then()
as all the callbacks are all invoked on a completion of a successful operation.
我一直在阅读有关 jQuery deferreds 和 promises 的文章,但我看不出使用.then()
&.done()
成功回调之间的区别。我知道Eric Hynds提到了这一点.done()
并.success()
映射到相同的功能,但我猜也是如此,.then()
因为所有回调都是在成功操作完成时调用的。
Can anyone please enlighten me to the correct usage?
任何人都可以请教我正确的用法吗?
回答by Julian Aubourg
The callbacks attached to done()
will be fired when the deferred is resolved. The callbacks attached to fail()
will be fired when the deferred is rejected.
done()
当 deferred 解决时,将触发附加到的回调。fail()
当延迟被拒绝时,将触发附加到的回调。
Prior to jQuery 1.8, then()
was just syntactic sugar:
在 jQuery 1.8 之前,then()
只是语法糖:
promise.then( doneCallback, failCallback )
// was equivalent to
promise.done( doneCallback ).fail( failCallback )
As of 1.8, then()
is an alias for pipe()
and returns a new promise, see herefor more information on pipe()
.
作为1.8,then()
是一个别名pipe()
,并返回一个新的承诺,看到这里的更多信息pipe()
。
success()
and error()
are only available on the jqXHR
object returned by a call to ajax()
. They are simple aliases for done()
and fail()
respectively:
success()
并且error()
仅在jqXHR
调用返回的对象上可用ajax()
。他们是简单的别名done()
和fail()
分别为:
jqXHR.done === jqXHR.success
jqXHR.fail === jqXHR.error
Also, done()
is not limited to a single callback and will filter out non-functions (though there is a bug with strings in version 1.8 that should be fixed in 1.8.1):
此外,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 );
Same goes for fail()
.
也一样fail()
。
回答by Lu4
There is also difference in way that return results are processed (its called chaining, done
doesn't chain while then
produces call chains)
返回结果的处理方式也有所不同(称为链接,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)
})
The following results will get logged:
将记录以下结果:
abc
123
undefined
While
尽管
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)
})
will get the following:
将得到以下内容:
abc
abc
abc
---------- Update:
- - - - - 更新:
Btw. I forgot to mention, if you return a Promise instead of atomic type value, the outer promise will wait until inner promise resolves:
顺便提一句。我忘了提,如果你返回一个 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
})
in this way it becomes very straightforward to compose parallel or sequential asynchronous operations such as:
通过这种方式,组合并行或顺序异步操作变得非常简单,例如:
// 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
})
The above code issues two http requests in parallel thus making the requests complete sooner, while below those http requests are being run sequentially thus reducing server load
上面的代码并行发出两个 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
})
回答by Marino ?imi?
.done()
has only one callback and it is the success callback
.done()
只有一个回调,它是成功回调
.then()
has both success and fail callbacks
.then()
有成功和失败回调
.fail()
has only one fail callback
.fail()
只有一个失败回调
so it is up to you what you must do... do you care if it succeeds or if it fails?
所以这取决于你必须做什么……你关心它是成功还是失败?
回答by Nipuna
deferred.done()
deferred.done()
adds handlers to be called only when Deferred is resolved. You can add multiple callbacks to be called.
添加仅在解决 Deferred 时才调用的处理程序。您可以添加多个要调用的回调。
var url = 'http://jsonplaceholder.typicode.com/posts/1';
$.ajax(url).done(doneCallback);
function doneCallback(result) {
console.log('Result 1 ' + result);
}
You can also write above like this,
上面也可以这样写,
function ajaxCall() {
var url = 'http://jsonplaceholder.typicode.com/posts/1';
return $.ajax(url);
}
$.when(ajaxCall()).then(doneCallback, failCallback);
deferred.then()
deferred.then()
adds handlers to be called when Deferred is resolved, rejected or still in progress.
添加要在 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);
}
回答by Dtipson
There is actually a pretty critical difference, insofar as jQuery's Deferreds are meant to be an implementations of Promises (and jQuery3.0 actually tries to bring them into spec).
实际上有一个非常关键的区别,因为 jQuery 的 Deferreds 是 Promises 的实现(而 jQuery3.0 实际上试图将它们带入规范)。
The key difference between done/then is that
done/then 之间的主要区别在于
.done()
ALWAYS returns the same Promise/wrapped values it started with, regardless of what you do or what you return..then()
always returns a NEW Promise, and you are in charge of controlling what that Promise is based on what the function you passed it returned.
.done()
无论您做什么或返回什么,始终返回与它开始时相同的 Promise/wrapped 值。.then()
总是返回一个新的 Promise,你负责根据你传递给它的函数返回什么来控制那个 Promise 是什么。
Translated from jQuery into native ES2015 Promises, .done()
is sort of like implementing a "tap" structure around a function in a Promise chain, in that it will, if the chain is in the "resolve" state, pass a value to a function... but the result of that function will NOT affect the chain itself.
从 jQuery 转换为原生 ES2015 Promises,.done()
有点像在 Promise 链中围绕函数实现“tap”结构,因为如果链处于“解析”状态,它将向函数传递一个值。 . 但该函数的结果不会影响链本身。
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));
Those will both log 5, not 6.
这些都将记录 5,而不是 6。
Note that I used done and doneWrap to do logging, not .then. That's because console.log functions don't actually return anything. And what happens if you pass .then a function that doesn't return anything?
请注意,我使用 done 和 doneWrap 来进行日志记录,而不是 .then。那是因为 console.log 函数实际上不返回任何东西。如果传递 .then 一个不返回任何内容的函数会发生什么?
Promise.resolve(5)
.then(doneWrap( x => x + 1))
.then(console.log.bind(console))
.then(console.log.bind(console));
That will log:
这将记录:
5
undefined
5
不明确的
What happened? When I used .then and passed it a function that didn't return anything, it's implicit result was "undefined"... which of course returned a Promise[undefined] to the next then method, which logged undefined. So the original value we started with was basically lost.
发生了什么?当我使用 .then 并向它传递一个不返回任何内容的函数时,它的隐含结果是“未定义”……当然,它向下一个记录未定义的 then 方法返回了一个 Promise[undefined]。所以我们开始的原始值基本上丢失了。
.then()
is, at heart, a form of function composition: the result of each step is used as the argument for the function in the next step. That's why .done is best thought of as a "tap"-> it's not actually part of the composition, just something that sneaks a look at the value at a certain step and runs a function at that value, but doesn't actually alter the composition in any way.
.then()
本质上是一种函数组合形式:每一步的结果用作下一步函数的参数。这就是为什么 .done 最好被认为是一个“tap”->它实际上不是组合的一部分,只是在某个步骤偷看值并在该值上运行函数的东西,但实际上并没有改变以任何方式组成。
This is a pretty fundamental difference, and there's a probably a good reason why native Promises don't have a .done method implemented themselves. We don't eve have to get into why there's no .fail method, because that's even more complicated (namely, .fail/.catch are NOT mirrors of .done/.then -> functions in .catch that return bare values do not "stay" rejected like those passed to .then, they resolve!)
这是一个非常根本的区别,并且可能有一个很好的理由说明原生 Promise 没有实现自己的 .done 方法。我们不必深究为什么没有 .fail 方法,因为那更复杂(也就是说,.fail/.catch 不是 .done/.then 的镜像 -> .catch 中返回裸值的函数不会“停留”被拒绝,就像传递给 .then 的那些一样,他们解决了!)
回答by JasmineOT
then()
always means it will be called in whatever case. But the parameters passing are different in different jQuery versions.
then()
总是意味着它会在任何情况下被调用。但是在不同的jQuery版本中传递的参数是不同的。
Prior to jQuery 1.8, then()
equals done().fail()
. And all of the callback functions share same parameters.
在 jQuery 1.8 之前,then()
等于done().fail()
. 并且所有回调函数共享相同的参数。
But as of jQuery 1.8, then()
returns a new promise, and if it has return a value, it will be passed into the next callback function.
但是从 jQuery 1.8 开始,then()
返回一个新的 promise,如果它有返回值,它将被传递到下一个回调函数中。
Let's see the following example:
让我们看下面的例子:
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 );
Prior to jQuery 1.8, the answer should be
在 jQuery 1.8 之前,答案应该是
result = 3
result = 3
result = 3
All result
takes 3. And then()
function always passes the same deferred object to the next function.
全部result
取 3。then()
函数总是将相同的延迟对象传递给下一个函数。
But as of jQuery 1.8, the result should be:
但是从 jQuery 1.8 开始,结果应该是:
result = 3
result = 7
result = NaN
Because the first then()
function returns a new promise, and the value 7 (and this is the only parameter that will passed on)is passed to the next done()
, so the second done()
write result = 7
. The second then()
takes 7 as the value of a
and takes undefined
as the value of b
, so the second then()
returns a new promise with the parameter NaN, and the last done()
prints NaN as its result.
因为第一个then()
函数返回一个新的 promise,并且值 7(这是将传递的唯一参数)传递给 next done()
,所以第二个done()
write result = 7
。第二个then()
将 7 作为 的值a
并undefined
作为 的值b
,因此第二个then()
返回一个带有参数 NaN 的新承诺,最后一个done()
打印 NaN 作为其结果。
回答by B M
There is a very simple mental mapping in response that was a bit hard to find in the other answers:
有一个非常简单的心理映射作为回应,在其他答案中很难找到:
done
implementstap
as in bluebird Promisesthen
implementsthen
as in ES6 Promises
done
实现tap
如bluebird Promisesthen
工具then
在ES6承诺
回答by Robert Siemer
Only use .then()
只使用 .then()
These are the disadvantages of .done()
这些是缺点 .done()
- can not be chained
- block
resolve()
call (all.done()
handlers will be executed synchronous) resolve()
might get an exception from registered.done()
handlers(!)- an exception in a
.done()
half-kills the deferred:- further
.done()
handlers will be silently skipped
- further
- 不能被链接
- 块
resolve()
调用(所有.done()
处理程序将同步执行) resolve()
可能会从注册的.done()
处理程序中得到一个异常(!).done()
半杀的例外延迟:- 其他
.done()
处理程序将被悄悄跳过
- 其他
I thought temporarily that .then(oneArgOnly)
always requires .catch()
so that no exception gets silently ignored, but that is not true any more: the unhandledrejection
event logs unhandled .then()
exceptions on the console (as default). Very reasonable! No reason left to use .done()
at all.
我暂时认为.then(oneArgOnly)
总是需要.catch()
这样就没有异常被默默忽略,但这不再是真的:unhandledrejection
事件.then()
在控制台上记录未处理的异常(默认情况下)。很合理!.done()
根本没有理由使用。
Proof
证明
The following code snippet reveals, that:
以下代码片段显示:
- all
.done()
handlers will be called synchronous at point ofresolve()
- logged as 1, 3, 5, 7
- logged before the script falls through bottom
- exception in a
.done()
influencesresolve()
caller- logged via catch around
resolve()
- logged via catch around
- exception breaks promise from further
.done()
resolution- 8 and 10 are not logged!
.then()
has none of these problems- logged as 2, 4, 6, 9, 11 after thread turns idle
- (snippet environment has no
unhandledrejection
is seems)
- 所有
.done()
处理程序将在resolve()
- 记录为 1, 3, 5, 7
- 在脚本跌落底部之前记录
.done()
影响resolve()
调用者的 异常- 通过捕获记录
resolve()
- 通过捕获记录
- 异常违背了进一步
.done()
解决的 承诺- 8 和 10 没有记录!
.then()
没有这些问题- 线程空闲后记录为 2, 4, 6, 9, 11
- (片段环境
unhandledrejection
似乎没有)
Btw, exceptions from .done()
can't be properly caught: because of the synchronous pattern of .done()
, the error is either thrown at the point of .resolve()
(might be library code!) or at the .done()
call which attaches the culprit if the deferred is already resolved.
顺便说一句,.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>
回答by schellmax
There's one more vital difference as of jQuery 3.0that can easily lead to unexpected behaviour and isn't mentioned in previous answers:
从jQuery 3.0 开始,还有一个更重要的区别,它很容易导致意外行为,并且在之前的答案中没有提到:
Consider the following code:
考虑以下代码:
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>
this will output:
这将输出:
then
now
Now, replace done()
by then()
in the very same snippet:
现在,在相同的代码段中替换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>
output is now:
输出现在是:
now
then
So, for immediatly resolved deferreds, the function passed to done()
will always be invoked in a synchronous manner, whereas any argument passed to then()
is invoked async.
因此,对于立即解决的延迟,传递给的函数done()
将始终以同步方式调用,而传递给的任何参数then()
都是异步调用的。
This differs from prior jQuery versions where both callbacks get called synchronously, as mentioned in the upgrade guide:
这与之前的 jQuery 版本不同,其中两个回调都被同步调用,如升级指南中所述:
Another behavior change required for Promises/A+ compliance is that Deferred .then() callbacks are always called asynchronously. Previously, if a .then() callback was added to a Deferred that was already resolved or rejected, the callback would run immediately and synchronously.
Promises/A+ 合规性所需的另一个行为更改是 Deferred .then() 回调始终异步调用。以前,如果将 .then() 回调添加到已经解决或拒绝的 Deferred,回调将立即同步运行。
回答by gleb bahmutov
.done()
terminates the promise chain, making sure nothing else can attach further steps. This means that the jQuery promise implementation can throw any unhandled exception, since no one can possible handle it using .fail()
.
.done()
终止承诺链,确保没有其他东西可以附加进一步的步骤。这意味着 jQuery promise 实现可以抛出任何未处理的异常,因为没有人可以使用.fail()
.
In practical terms, if you do not plan to attach more steps to a promise, you should use .done()
. For more details see why promises need to be done
实际上,如果您不打算为承诺附加更多步骤,则应使用.done()
. 有关更多详细信息,请参阅为什么需要完成承诺