web-dev-qa-db-ja.com

jQuery Deferred、$。when()およびfail()コールバック引数

延期された操作の1つが成功しないときに、$.when()を使用すると、予期しない結果が発生します。

2つの遅延を作成したこの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]));
    });
_

自分で実行します: http://jsfiddle.net/r2d3j/2/

_fail! f1, f2: ["456 from f2", null]_を取得します

問題は、.fail()コールバックで、f2()拒否で渡された値が、_f1Value_を期待する最初の引数にルーティングされていることです。つまり、どの遅延オブジェクトが実際にそのreject()を投稿したかを知る方法が実際にはなく、障害データが実際にどの操作に属しているかもわかりません。

最初の延期が失敗しなかったので、.fail()が引数_null, '456 from f2'_を取得することを期待していました。それとも私はここで延期を正しく行っていないのですか?

コールバックの引数の順序が尊重されていない場合、どの延期が失敗し、どの拒否引数がどの失敗した延期に属しているかを知るにはどうすればよいですか?

23
Alex Wayne

内部的には、「拒否」パスと「失敗」パスは2つの完全に別個のキューによって処理されるため、期待どおりに機能しません。

「when()」グループからどの元のDeferredが失敗したかを知るために、オブジェクトリテラルなどの一部として「.reject()」呼び出しとともにそれらを渡すことができます。

5
Pointy

$.when()は、パラメーターのいずれかが失敗した場合、失敗したコールバック(2番目のパラメーターがthen()に渡される)をすぐに実行します。これは仕様によるものです。ドキュメントを引用するには:

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

Deferredの1つが拒​​否された複数のDeferredの場合、jQuery.whenはマスターDeferredのfailCallbacksをすぐに起動します。一部の延期は、その時点でまだ解決されていない可能性があることに注意してください。この場合、未完了のajaxリクエストをキャンセルするなど、追加の処理を実行する必要がある場合は、基になるjqXHRオブジェクトへの参照をクロージャに保持し、failCallbackでそれらを検査/キャンセルできます。

実際には、成功/失敗のステータスに関係なく、すべてが終了するまで待機するコールバックを取得する組み込みの方法はありません。

だから、私はあなたのために$.whenAll()を作りました:)
いずれかの方法で、すべてが解決するまで常に待機します。

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

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

私はこれと同じ問題に直面しました。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");
          });
      });
  });

DeleteFileメソッドは、.doneまたは.failコールバックを持つpromiseを返します。

これにより、すべての延期が完了した後にアクションを実行できます。私の場合、ファイル削除エラーの概要を表示します。

これを試したところ、残念ながら、インターバルタイマーを設定して、遅延オブジェクトの$ .eachの後にすべてが本当に完了したことを確認する必要がありました。これは奇妙で直感に反しているようです。

まだこれらの延期を理解しようとしています!

0
ScottE

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

複数の入力が与えられた場合、これは常に拒否されます。 rejected = true;rejected |= reject;である必要があります

0
Alex Davidson