jQuery的推迟和承诺 – .then()与.done()
我一直在阅读关于jQuery的延期和承诺,我看不出使用.then()
和.done()
成功的回调之间的差异。 我知道Eric Hynds提到.done()
和.success()
映射到相同的功能,但我猜也是.then()
所有的回调都是在完成一个成功的操作时调用的。
任何人都可以请赐教正确的用法?
非常感谢
附加到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()
只在通过调用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()
。
处理返回结果的方式也有所不同(称为链接, done
链接不生成链接)
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()
只有一个回调,它是成功的回调
.then()
有成功和失败的回调
.fail()
只有一个失败回调
所以这取决于你自己该做什么…你关心它是否成功或失败了吗?
deferred.done()
添加处理程序只有在解决延迟时才被调用。 你可以添加多个回调被调用。
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.then()
添加处理程序,以便在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); }
实际上有一个非常关键的区别,因为jQuery的Deferreds是为了实现Promise(而jQuery3.0实际上是试图把它们引入规范)。
完成/之后的关键区别在于
-
.done()
不管你做什么或返回什么,ALWAYS都会返回与之前相同的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做日志记录,而不是。 这是因为console.log函数实际上不会返回任何东西。 如果你传递了一个函数,那么返回什么呢?
Promise.resolve(5) .then(doneWrap( x => x + 1)) .then(console.log.bind(console)) .then(console.log.bind(console));
这将记录:
五
未定义
发生了什么? 当我使用.then并传递了一个函数,它没有返回任何东西,它隐含的结果是“未定义”…这当然返回一个Promise [undefined]到下一个然后方法,记录未定义。 所以我们开始的原始价值基本上已经丧失了。
.then()
是一个函数组合形式:每一步的结果在下一步中被用作函数的参数。 这就是为什么.done最好被认为是一个“tap” – 它实际上并不是构图的一部分,只是在某个步骤中偷偷地看着这个值,然后运行一个函数,但是并没有真正改变任何方式的组成。
这是一个非常基本的区别,为什么本地Promise没有自己实现的.done方法可能是一个很好的理由。 我们不必深入了解为什么没有.fail方法,因为它更加复杂(即,.fail / .catch不是.done / .then – > .catch中的函数的镜像,它返回的是裸值“停留”被拒绝像那些传给他们的人,然后他们解决!)
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作为结果。
那是我的答案。
var deff = $.Deferred(); deff.then(function(){ alert('ok'); var deff = $.Deferred(); setTimeout(function(){ deff.resolve() }, 1000) return deff; }).then(function () { alert('ok2') }) deff.resolve()
这是独特的功能。 它可以防止回调地狱。
.done()
终止承诺链,确保没有别的可以附加进一步的步骤。 这意味着jQuery承诺实现可以抛出任何未处理的异常,因为没有人可以使用.fail()
来处理它。
实际上,如果你不打算附加更多的步骤来承诺,你应该使用.done()
。 欲了解更多细节,请看为什么承诺需要完成