我一直在阅读jQuery的延迟和承诺,我看不出使用.then()和amp;.done()用于成功回调。我知道Eric Hynds提到.done()和.success()映射到相同的功能,但我猜.then()也是如此,因为所有的回调都是在一个成功的操作完成时调用的。
.then()
.done()
.success()
有人能告诉我正确的用法吗?
.done()只有一个回调,它是成功回调
.then()有成功回调和失败回调
.fail()只有一个失败的回调
.fail()
所以你该怎么做就怎么做了…你在乎成功还是失败吗?
附加到done()的回调将在解析延迟时被触发。附加到fail()的回调函数将在被拒绝时被触发。
done()
fail()
在jQuery 1.8之前,then()只是语法糖:
then()
promise.then( doneCallback, failCallback ) // was equivalent to promise.done( doneCallback ).fail( failCallback )
从1.8开始,then()是pipe()的别名,并返回一个新的promise,有关pipe()的更多信息,请参阅在这里。
pipe()
success()和error()只在调用ajax()返回的jqXHR对象上可用。它们分别是done()和fail()的别名:
success()
error()
ajax()
jqXHR
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()也是一样。
返回结果的处理方式也有所不同(它被称为链接,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将等待内部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()终止承诺链,确保没有其他步骤可以附加。这意味着jQuery承诺实现可以抛出任何未处理的异常,因为没有人可以使用.fail()处理它。
在实践中,如果你不打算给承诺附加更多的步骤,你应该使用.done()。更多细节见为什么承诺需要兑现
添加名为仅当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()总是意味着在任何情况下都会被调用。但是在不同的jQuery版本中传递的参数是不同的。
在jQuery 1.8之前,then()等于done().fail()。所有的回调函数共享相同的参数。
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()函数总是将相同的延迟对象传递给下一个函数。
result
但是从jQuery 1.8开始,结果应该是:
result = 3 result = 7 result = NaN
因为第一个then()函数返回一个新的承诺,并且值7(这是将传递的唯一参数)被传递给下一个done(),因此第二个done()写result = 7。第二个then()将7作为a的值,并将undefined作为b的值,因此第二个then()返回一个带有参数NaN的新promise,最后一个done()打印NaN作为其结果
result = 7
a
undefined
b
实际上有一个非常关键的区别,因为jQuery的deferred是承诺的实现(query3.0实际上试图将它们纳入规范)。
完成/然后之间的关键区别是
从jQuery翻译到ES2015 Promises, .done()有点像实现一个"tap"在Promise链中围绕一个函数构建结构,如果该链在"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 未定义的
5
未定义的
发生了什么事?当我使用。then并传递给它一个不返回任何东西的函数时,它的隐式结果是“undefined”…它当然会返回一个Promise[undefined]给next then方法,该方法的日志为undefined。所以我们开始时的初始值基本失去了。
.then()本质上是函数组合的一种形式:每一步的结果都被用作下一步函数的实参。这就是为什么done最好被认为是“tap”;它实际上不是组合的一部分,只是在某一步偷偷查看值并在该值上运行一个函数,但实际上不以任何方式改变组合。
这是一个非常基本的区别,可能有一个很好的原因,为什么本机promise没有实现自己的.done方法。我们不需要讨论为什么没有.fail方法,因为它更复杂(即.fail/。catch不是.done/的镜像。然后→.catch中返回裸值的函数不会“;stay”;像那些被拒绝的人一样,然后,他们解决了!)
在回答中有一个非常简单的心理映射,在其他答案中很难找到:
done实现tap的方式如下:bluebird Promises
tap
then实现then,如ES6 Promises
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>
this will output:
then now
现在,在相同的代码片段中,用then()替换done():
var d = $.Deferred(); d.then(() => console.log('then')); d.resolve(); console.log('now');
output is now:
now then
因此,对于立即解决的延迟,传递给done()的函数将始终以同步方式调用,而传递给then()的任何参数将以异步方式调用。
这与之前的jQuery版本不同,之前的jQuery版本同步调用两个回调函数,如升级指南中所述:
遵守承诺/A+要求的另一个行为改变是 Deferred .then()回调总是异步调用的。 以前,如果.then()回调被添加到Deferred that was 已经解决或拒绝,回调将立即运行 同步。< / p >
.done()没有优点,缺点如下:
a.done().done()
a.done(); a.done()
a.then(); a.then()
a.then().then()
resolve()
我暂时认为.then(oneArgOnly)总是需要.catch(),所以没有异常被无声地忽略,但这不再是真的:unhandledrejection事件记录控制台上未处理的.then()异常(默认)。很合理的!根本没有理由使用.done()。
.then(oneArgOnly)
.catch()
unhandledrejection
下面的代码片段显示:
顺便说一句,来自.done()的异常不能被正确捕获:由于.done()的同步模式,错误要么在.resolve()点抛出(可能是库代码!),要么在.done()调用抛出,如果延迟已经解决,则在附加罪犯的.done()调用抛出。
.resolve()
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>
除了以上的答案:
then的真正强大之处在于能够以一种流畅的方式连接ajax调用,从而避免回调地狱。
例如:
$.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后面