jQuery延迟和承诺 - .then()vs .done()

scr*_*key 458 jquery promise jquery-deferred

我一直在阅读jQuery延迟和承诺,我看不出使用.then()&.done()成功回调之间的区别.我知道Eric Hynds提到.done().success()映射到相同的功能,但我猜是这样做的,.then()因为所有的回调都是在成功操作完成时调用的.

任何人都可以请你告诉我正确的用法?

Jul*_*urg 562

done()解析延迟时将触发附加的回调.fail()当延迟被拒绝时,将触发附加的回调.

在jQuery 1.8之前,then()只是语法糖:

promise.then( doneCallback, failCallback )
// was equivalent to
promise.done( doneCallback ).fail( failCallback )
Run Code Online (Sandbox Code Playgroud)

从1.8开始,then()是一个别名pipe()并返回一个新的承诺,有关详细信息,请参阅此处pipe().

success()并且error()仅在jqXHR调用返回的对象上可用ajax().他们是简单的别名done()fail()分别为:

jqXHR.done === jqXHR.success
jqXHR.fail === jqXHR.error
Run Code Online (Sandbox Code Playgroud)

此外,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 );
Run Code Online (Sandbox Code Playgroud)

同样如此fail().

  • 你能不能写一下jQuery文档,请:-). (58认同)
  • +1 - 感谢您的回复.你是否同样​​为jQuery完成AJAX重写的Julian Aubourg? (51认同)
  • "然后"返回一个新的承诺是我失踪的关键.我无法理解为什么像`$ .get(....).done(function(data1){return $ .get(...)}).done(function(data2){...} ``失败,`data2`未定义; 当我把`done`改为`then`时它起作用了,因为我真的想把管道承诺放在一起而不是把更多的处理程序附加到最初的承诺上. (8认同)
  • jQuery 3.0是第一个符合Promises/A +和ES2015规范的版本. (5认同)
  • 我仍然不明白为什么我要在另一个上使用一个。如果我进行了ajax调用,并且我需要等到该调用完全完成(这意味着从服务器返回响应)之后再调用另一个ajax调用,我应该使用`done`还是`then`?为什么? (2认同)

Lu4*_*Lu4 405

处理返回结果的方式也有所不同(它称为链接,donethen生成调用链时不链接)

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)
})
Run Code Online (Sandbox Code Playgroud)

将记录以下结果:

abc
123
undefined
Run Code Online (Sandbox Code Playgroud)

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)
})
Run Code Online (Sandbox Code Playgroud)

将获得以下内容:

abc
abc
abc
Run Code Online (Sandbox Code Playgroud)

----------更新:

顺便说一句.我忘了提一下,如果你返回一个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
})
Run Code Online (Sandbox Code Playgroud)

通过这种方式,组合并行或顺序异步操作变得非常简单,例如:

// 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
})
Run Code Online (Sandbox Code Playgroud)

上面的代码并行发出两个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
})
Run Code Online (Sandbox Code Playgroud)

  • +1表示'done`对结果没有做什么,`then`会改变结果.其他人错过了巨大的一点. (116认同)
  • 可能值得一提的是这适用于jQuery的哪个版本,因为`then`的行为在1.8中发生了变化 (9认同)
  • 上面的例子还强调'done'适用于最初创建的原始promise对象,但'then'返回一个新的promise. (7认同)
  • +1直截了当.我创建了一个[runnable example](https://gist.github.com/mkropat/8811708),如果有人想看看混合了`done`和`then`调用的链是什么. (4认同)
  • 这适用于jQuery 1.8+.较旧的版本就像`done`一样.在1.8之前将`then`更改为`pipe`以获得1.8+`then`行为. (2认同)
  • 此外,jQuery 3.0是第一个符合ES2015和Promises/A +规范的版本. (2认同)

Mar*_*mić 56

.done() 只有一个回调,它是成功的回调

.then() 既有成功也有失败的回调

.fail() 只有一个失败的回调

所以取决于你必须做的事情......你关心它是否成功或是否失败?

  • 你没有提到'那么'会产生调用链.见Lu4的答案. (18认同)

Nip*_*una 15

deferred.done()

添加处理程序仅在解析Deferred时调用.您可以添加要调用的多个回调.

var url = 'http://jsonplaceholder.typicode.com/posts/1';
$.ajax(url).done(doneCallback);

function doneCallback(result) {
    console.log('Result 1 ' + result);
}
Run Code Online (Sandbox Code Playgroud)

你也可以这样写,

function ajaxCall() {
    var url = 'http://jsonplaceholder.typicode.com/posts/1';
    return $.ajax(url);
}

$.when(ajaxCall()).then(doneCallback, failCallback);
Run Code Online (Sandbox Code Playgroud)

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);
}
Run Code Online (Sandbox Code Playgroud)


Dti*_*son 9

实际上存在一个相当重要的区别,因为jQuery的Deferreds意味着是Promises的实现(并且jQuery3.0实际上试图将它们引入规范).

完成/之后的关键区别在于

  • .done() 无论您做什么或返回什么,总是返回它开始时的相同Promise /包装值.
  • .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));
Run Code Online (Sandbox Code Playgroud)

那些将记录5,而不是6.

请注意,我使用done和doneWrap来进行日志记录,而不是.then.那是因为console.log函数实际上并没有返回任何内容.如果你通过了会发生什么.然后一个不返回任何东西的函数?

Promise.resolve(5)
       .then(doneWrap( x => x + 1))
       .then(console.log.bind(console))
       .then(console.log.bind(console));
Run Code Online (Sandbox Code Playgroud)

那会记录:

未定义

发生了什么?当我使用.then并传递一个没有返回任何内容的函数时,它的隐式结果是"未定义的"......当然,它将Promise [undefined]返回到下一个方法,该方法记录为undefined.所以我们开始的原始价值基本上已经失去了.

.then()从本质上讲,它是一种函数组合形式:每一步的结果都被用作下一步中函数的参数.这就是为什么.done最好被认为是一个"点击" - >它实际上并不是组合的一部分,只是在某个步骤偷看某个值并在该值上运行一个函数的东西,但实际上并没有改变任何方式的组成.

这是一个非常基本的区别,并且可能有一个很好的理由为什么本机Promise没有自己实现的.done方法.我们不必讨论为什么没有.fail方法,因为它更复杂(即.fail/.catch不是.done/.then的镜像 - > .catch中的函数返回裸值不"停留"被拒绝,就像传递给那些人一样.然后,他们解决了!)


Jas*_*eOT 5

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 );
Run Code Online (Sandbox Code Playgroud)

在jQuery 1.8之前,答案应该是

result = 3
result = 3
result = 3
Run Code Online (Sandbox Code Playgroud)

全部result花费3。then()函数始终将相同的延迟对象传递给下一个函数。

但是从jQuery 1.8开始,结果应该是:

result = 3
result = 7
result = NaN
Run Code Online (Sandbox Code Playgroud)

因为第一个then()函数返回一个新的Promise,并且值7(这是将传递的唯一参数)被传递给下一个done(),所以第二个done()write result = 7。第二个then()取值为7,a而取值undefinedb,因此第二个then()返回参数为NaN的新诺言,最后一个done()输出NaN作为结果。


Rob*_*mer 5

仅使用.then()

\n

.done()没有优点,缺点如下:

\n
    \n
  • 无法正确链接\n
      \n
    • a.done().done()是相同的a.done(); a.done(),可以用以下方式估计a.then(); a.then()
    • \n
    • a.then().then()不可能与.done()
    • \n
    \n
  • \n
  • resolve()调用(所有.done()处理程序将同步执行)
  • \n
  • resolve()可能会从注册的处理程序中得到异常.done()(!)
  • \n
  • half中的异常.done()会导致延迟终止:\n
      \n
    • 更多.done()处理程序将被静默跳过
    • \n
    \n
  • \n
  • .then()没有任何这些问题
  • \n
\n

我暂时认为.then(oneArgOnly)总是需要.catch()这样,这样就不会默默地忽略任何异常,但这不再是这样:事件在控制台上unhandledrejection记录未处理的异常(默认情况下)。.then()很有道理!.done()根本没有理由使用。

\n

证明

\n

下面的代码片段揭示了:

\n
    \n
  • 所有处理程序将在\n.done()点同步调用resolve()
      \n
    • 记录为 1、3、5、7
    • \n
    • 在脚本跌入底部之前记录
    • \n
    \n
  • \n
  • .done()影响调用者中的异常resolve()\n
      \n
    • 通过 catch around 记录resolve()
    • \n
    \n
  • \n
  • 异常违背了进一步解决方案的承诺.done()\n
      \n
    • 8 和 10 未记录!
    • \n
    \n
  • \n
  • .then()没有这些问题\n
      \n
    • 线程空闲后记录为 2、4、6、9、11
    • \n
    • (片段环境似乎没有unhandledrejection
    • \n
    \n
  • \n
\n

顺便说一句,来自 can\xe2\x80\x99t 的异常.done()可以被正确捕获:由于 的同步模式,错误要么在(可能是库代码!).done()处抛出,要么在附加罪魁祸首的调用处抛出,如果延迟是已经解决了。.resolve().done()

\n

\r\n
\r\n
console.log(\'Start of script.\');\nlet deferred = $.Deferred();\n// deferred.resolve(\'Redemption.\');\ndeferred.fail(() => console.log(\'fail()\'));\ndeferred.catch(()=> console.log(\'catch()\'));\ndeferred.done(() => console.log(\'1-done()\'));\ndeferred.then(() => console.log(\'2-then()\'));\ndeferred.done(() => console.log(\'3-done()\'));\ndeferred.then(() =>{console.log(\'4-then()-throw\');\n    throw \'thrown from 4-then()\';});\ndeferred.done(() => console.log(\'5-done()\'));\ndeferred.then(() => console.log(\'6-then()\'));\ndeferred.done(() =>{console.log(\'7-done()-throw\');\n    throw \'thrown from 7-done()\';});\ndeferred.done(() => console.log(\'8-done()\'));\ndeferred.then(() => console.log(\'9-then()\'));\n\nconsole.log(\'Resolving.\');\ntry {\n    deferred.resolve(\'Solution.\');\n} catch(e) {\n    console.log(`Caught exception from handler\n        in resolve():`, e);\n}\ndeferred.done(() => console.log(\'10-done()\'));\ndeferred.then(() => console.log(\'11-then()\'));\nconsole.log(\'End of script.\');
Run Code Online (Sandbox Code Playgroud)\r\n
<script\nsrc="https://code.jquery.com/jquery-3.4.1.min.js"\nintegrity="sha384-vk5WoKIaW/vJyUAd9n/wmopsmNhiy+L2Z+SBxGYnUkunIxVxAv/UtMOhba/xskxh"\ncrossorigin="anonymous"\n></script>
Run Code Online (Sandbox Code Playgroud)\r\n
\r\n
\r\n

\n