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

时间:2011-03-25 18:02:49

标签: jquery promise jquery-deferred

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

任何人都可以告诉我正确的使用方法吗?

11 个答案:

答案 0 :(得分:551)

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

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

promise.then( doneCallback, failCallback )
// was equivalent to
promise.done( doneCallback ).fail( failCallback )

从1.8开始,then()pipe()的别名并返回新的承诺,有关pipe()的更多信息,请参阅here

success()error()仅适用于jqXHR调用返回的ajax()对象。它们分别是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()

答案 1 :(得分:391)

处理返回结果的方式也有所不同(称为链接,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
})

答案 2 :(得分:51)

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

.then()同时有成功和失败的回调

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

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

答案 3 :(得分:13)

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()

在延期解决,拒绝或仍在进行中时,将处理程序称为

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);
}

答案 4 :(得分:8)

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

完成/之后的关键区别是

  • .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函数实际上并没有返回任何内容。如果你通过,那会发生什么。然后一个不返回任何东西的函数?

Promise.resolve(5)
       .then(doneWrap( x => x + 1))
       .then(console.log.bind(console))
       .then(console.log.bind(console));

那将记录:

  

5

     

未定义

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

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

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

答案 5 :(得分:4)

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()a的值设为7,并将undefined作为b的值,因此第二个then()将返回带有参数的新保证NaN和最后done()打印NaN作为结果。

答案 6 :(得分:2)

仅使用.then()

这些是.done()

的缺点
  • 无法链接
  • 阻止resolve()调用(所有.done()处理程序将同步执行)
  • resolve()可能会从注册的.done()处理程序中获得异常(!)
  • .done()中的异常会杀死延迟者:
    • 进一步.done()处理程序将被静默跳过

我暂时认为.then(oneArgOnly)总是需要.catch(),因此不会无声地忽略任何异常,但这不再是事实:unhandledrejection事件记录了未处理的.then()异常在控制台上(默认)。很合理!完全没有理由使用.done()

证明

以下代码段揭示了这一点,

  • 所有.done()处理程序将在resolve()点被称为同步处理程序
    • 记录为1、3、5、7
    • 在脚本跌入谷底之前记录
  • .done()中的
  • 例外会影响resolve()呼叫者
    • 通过resolve()赶上来记录
  • 例外违反了进一步.done()解决方案的承诺
    • 8和10未记录!
  • .then()没有这些问题
    • 线程空闲后记录为2、4、6、9、11
    • (片段环境似乎没有unhandledrejection

顺便说一句,.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>

答案 7 :(得分:1)

在回答中有一个非常简单的心理映射,在其他答案中有点难以找到:

  • tap实施then,如bluebird Promises

  • then实施<div class="primary-colors"> <h3>Primary Colors</h3> <div class="primary-range-container"> <input type="range" min="1" max="15" value ="5" class="primary-slider"><span class="value">0</span> </div> <hr> <div class="color-container"> <div class="color primary-color-1"></div> <div class="color primary-color-2"></div> <div class="color primary-color-3"></div> <div class="color primary-color-4"></div> <div class="color primary-color-5"></div> <div class="color primary-color-6"></div> <div class="color primary-color-7"></div> <div class="color primary-color-8"></div> <div class="color primary-color-9"></div> <div class="color primary-color-10"></div> <div class="color primary-color-11"></div> </div> </div> ,如ES6 Promises

答案 8 :(得分:0)

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()的任何参数均异步调用。

这与先前的jQuery版本不同,在jQuery中,两个回调都被同步调用,如upgrade guide中所述:

  

遵守Promises / A +要求的另一个行为更改是   延迟的.then()回调始终被异步调用。   以前,如果将.then()回调添加到Deferred中,则   已经解决或被拒绝,回调将立即运行,   同步。

答案 9 :(得分:0)

除了上面的答案:

.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

答案 10 :(得分:-4)

.done()终止了承诺链,确保没有其他任何东西可以附加进一步的步骤。这意味着jQuery promise实现可以抛出任何未处理的异常,因为没有人可以使用.fail()来处理它。

实际上,如果您不打算在承诺中附加更多步骤,则应使用.done()。有关详细信息,请参阅why promises need to be done