jQuery Deferred,$ .when()和fail()回调参数

时间:2022-08-23 11:38:00

I'm getting an unexpected result when using $.when() when one of the deferred operations does not succeed.

当其中一个延迟操作不成功时使用$ .when()时,我得到一个意外的结果。

Take this JavaScript, which created 2 deferreds. The first one succeeds and the second one fails.

拿这个JavaScript,创建2个延迟。第一个成功,第二个失败。

var f1 = function() {
    return $.Deferred(function(dfd) {
        dfd.resolve('123 from f1');
    }).promise();
};

var f2 = function() {
    return $.Deferred(function(dfd) {
        dfd.reject('456 from f2');
    }).promise();
};

$.when(f1(), f2())
    .then(function(f1Val, f2Val) {
        alert('success! f1, f2: ' + JSON.stringify([f1Val, f2Val]));
    })
    .fail(function(f1Val, f2Val) {
        alert('fail!    f1, f2: ' + JSON.stringify([f1Val, f2Val]));
    });

Run it yourself: http://jsfiddle.net/r2d3j/2/

自己运行:http://jsfiddle.net/r2d3j/2/

I get fail! f1, f2: ["456 from f2", null]

我失败了! f1,f2:[“456 from f2”,null]

The problem is that in the .fail() callback the value passed with the f2() rejection, is being routed to the first argument, where i expect the f1Value. Which means that I don't really have a way of know which deferred object actually posted that reject(), and I also dont know which operation that failure data actually belongs to.

问题是在.fail()回调中,使用f2()拒绝传递的值被路由到第一个参数,其中我期望f1Value。这意味着我真的没有办法知道哪个延迟对象实际发布了reject(),而且我也不知道故障数据实际属于哪个操作。

I would have expected that .fail() would get arguments null, '456 from f2' since the first deferred did not fail. Or am I just not doing deferreds right way here?

我原以为.fail()会得到参数null,'f6'来自456,因为第一个延迟没有失败。或者我只是没有在这里做正确的延期?

How do I know which deferreds failed, and which rejection arguments belong to which failed deferred if the argument order in the callback is not respected?

如果不遵守回调中的参数顺序,我如何知道哪些延迟失败,以及哪些拒绝参数属于哪个失败延迟?

4 个解决方案

#1


5  

Internally, the "reject" and "fail" paths are handled by two totally separate queues, so it just doesn't work the way you expect.

在内部,“拒绝”和“失败”路径由两个完全独立的队列处理,因此它不会按照您期望的方式工作。

In order to know which original Deferred failed from the "when()" group, you could have them pass themselves along with the ".reject()" call as part of an object literal or something.

为了从“when()”组中知道哪个原始Deferred失败,您可以将它们与“.reject()”调用一起传递为对象文字或其他内容的一部分。

#2


21  

$.when() will execute the failed callback (2nd parameter passed to then()) immediately if any one of the parameters fails. It's by design. To quote the documentation:

如果任何一个参数失败,$ .when()将立即执行失败的回调(传递给then()的第二个参数)。这是设计的。引用文档:

http://api.jquery.com/jQuery.when/

http://api.jquery.com/jQuery.when/

In the multiple-Deferreds case where one of the Deferreds is rejected, jQuery.when immediately fires the failCallbacks for its master Deferred. Note that some of the Deferreds may still be unresolved at that point. If you need to perform additional processing for this case, such as canceling any unfinished ajax requests, you can keep references to the underlying jqXHR objects in a closure and inspect/cancel them in the failCallback.

在多个Deferreds案例中,其中一个Deferreds被拒绝,jQuery.when会立即触发其主Deferred的failCallbacks。请注意,此时某些延迟可能仍未解决。如果需要对此情况执行其他处理,例如取消任何未完成的ajax请求,则可以在闭包中保留对基础jqXHR对象的引用,并在failCallback中检查/取消它们。

There's actually no built-in way of getting a callback that waits untils all of them are finished regardless of their success/failure status.

实际上没有内置的获取回调的方法,无论其成功/失败状态如何,都会等待所有回调完成。

So, I built a $.whenAll() for you :)
It always waits until all of them resolve, one way or the other:

所以,我为你建了一个$ .whenAll():)它总是等到所有这些都以某种方式解决:

http://jsfiddle.net/InfinitiesLoop/yQsYK/51/

http://jsfiddle.net/InfinitiesLoop/yQsYK/51/

$.whenAll(a, b, c)
    .then( callbackUponAllResolvedOrRejected );

#3


0  

I've faced this same problem, and I dealt with it by using the .always callback and inspecting my array of deferred objects. I had an unknown number of ajax calls, so I had to do the following:

我遇到了同样的问题,我通过使用.always回调并检查我的延迟对象数组来处理它。我有一个未知数量的ajax调用,所以我必须做以下事情:

// array of ajax deletes
var deletes = [];
$checkboxes.each(function () {
    deletes.push(deleteFile(this));
});

$.when.apply($, deletes)
  .always(function () {
      // unfortunately .fail shortcircuits and returns the first fail,
      // so we have to loop the deferred objects and see what happened.

      $.each(deletes, function () {
          this.done(function () {
              console.log("done");
          }).fail(function () {
              console.log("fail");
          });
      });
  });

The deleteFile method returns a promise, which has .done or .fail callbacks.

deleteFile方法返回一个promise,它有.done或.fail回调。

This allows you to take action after all deferreds have completed. In my case I'm going to show a delete file error summary.

这允许您在所有延迟完成后采取措施。在我的情况下,我将显示删除文件错误摘要。

I just tried this, and unfortunately I had to put a interval timer to check that they were all truly done after my $.each on the deferred objects. This seems odd and counterintuitive.

我只是尝试了这个,不幸的是我不得不设置一个间隔计时器来检查它们是否都是在延迟对象的$ .each之后才真正完成的。这看似奇怪而且违反直觉。

Still trying to understand these deferreds!

仍在努力了解这些延期!

#4


0  

http://jsfiddle.net/InfinitiesLoop/yQsYK/

http://jsfiddle.net/InfinitiesLoop/yQsYK/

This will always reject if given multiple inputs. rejected = true; should be rejected |= reject;

如果给定多个输入,这将始终拒绝。 reject = true;应该被拒绝| =拒绝;

#1


5  

Internally, the "reject" and "fail" paths are handled by two totally separate queues, so it just doesn't work the way you expect.

在内部,“拒绝”和“失败”路径由两个完全独立的队列处理,因此它不会按照您期望的方式工作。

In order to know which original Deferred failed from the "when()" group, you could have them pass themselves along with the ".reject()" call as part of an object literal or something.

为了从“when()”组中知道哪个原始Deferred失败,您可以将它们与“.reject()”调用一起传递为对象文字或其他内容的一部分。

#2


21  

$.when() will execute the failed callback (2nd parameter passed to then()) immediately if any one of the parameters fails. It's by design. To quote the documentation:

如果任何一个参数失败,$ .when()将立即执行失败的回调(传递给then()的第二个参数)。这是设计的。引用文档:

http://api.jquery.com/jQuery.when/

http://api.jquery.com/jQuery.when/

In the multiple-Deferreds case where one of the Deferreds is rejected, jQuery.when immediately fires the failCallbacks for its master Deferred. Note that some of the Deferreds may still be unresolved at that point. If you need to perform additional processing for this case, such as canceling any unfinished ajax requests, you can keep references to the underlying jqXHR objects in a closure and inspect/cancel them in the failCallback.

在多个Deferreds案例中,其中一个Deferreds被拒绝,jQuery.when会立即触发其主Deferred的failCallbacks。请注意,此时某些延迟可能仍未解决。如果需要对此情况执行其他处理,例如取消任何未完成的ajax请求,则可以在闭包中保留对基础jqXHR对象的引用,并在failCallback中检查/取消它们。

There's actually no built-in way of getting a callback that waits untils all of them are finished regardless of their success/failure status.

实际上没有内置的获取回调的方法,无论其成功/失败状态如何,都会等待所有回调完成。

So, I built a $.whenAll() for you :)
It always waits until all of them resolve, one way or the other:

所以,我为你建了一个$ .whenAll():)它总是等到所有这些都以某种方式解决:

http://jsfiddle.net/InfinitiesLoop/yQsYK/51/

http://jsfiddle.net/InfinitiesLoop/yQsYK/51/

$.whenAll(a, b, c)
    .then( callbackUponAllResolvedOrRejected );

#3


0  

I've faced this same problem, and I dealt with it by using the .always callback and inspecting my array of deferred objects. I had an unknown number of ajax calls, so I had to do the following:

我遇到了同样的问题,我通过使用.always回调并检查我的延迟对象数组来处理它。我有一个未知数量的ajax调用,所以我必须做以下事情:

// array of ajax deletes
var deletes = [];
$checkboxes.each(function () {
    deletes.push(deleteFile(this));
});

$.when.apply($, deletes)
  .always(function () {
      // unfortunately .fail shortcircuits and returns the first fail,
      // so we have to loop the deferred objects and see what happened.

      $.each(deletes, function () {
          this.done(function () {
              console.log("done");
          }).fail(function () {
              console.log("fail");
          });
      });
  });

The deleteFile method returns a promise, which has .done or .fail callbacks.

deleteFile方法返回一个promise,它有.done或.fail回调。

This allows you to take action after all deferreds have completed. In my case I'm going to show a delete file error summary.

这允许您在所有延迟完成后采取措施。在我的情况下,我将显示删除文件错误摘要。

I just tried this, and unfortunately I had to put a interval timer to check that they were all truly done after my $.each on the deferred objects. This seems odd and counterintuitive.

我只是尝试了这个,不幸的是我不得不设置一个间隔计时器来检查它们是否都是在延迟对象的$ .each之后才真正完成的。这看似奇怪而且违反直觉。

Still trying to understand these deferreds!

仍在努力了解这些延期!

#4


0  

http://jsfiddle.net/InfinitiesLoop/yQsYK/

http://jsfiddle.net/InfinitiesLoop/yQsYK/

This will always reject if given multiple inputs. rejected = true; should be rejected |= reject;

如果给定多个输入,这将始终拒绝。 reject = true;应该被拒绝| =拒绝;