23

$.when()遅延操作の 1 つが成功しない場合に使用すると、予期しない結果が得られます。

2 つの deferred を作成したこの JavaScript を見てください。1 つ目は成功し、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()また、失敗データが実際にどの操作に属しているかもわかりません。

最初の deferred が失敗しなかったので、.fail()引数を取得すると予想していました。null, '456 from f2'それとも、ここで遅延処理を正しく行っていないだけですか?

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

4

4 に答える 4

27

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

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

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

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

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

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

$.whenAll(a, b, c)
    .then( callbackUponAllResolvedOrRejected );
于 2011-10-24T20:41:38.557 に答える
4

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

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

于 2011-04-01T20:13:51.593 に答える
0

私はこれと同じ問題に直面しており、.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 を返します。

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

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

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

于 2012-02-17T16:01:34.690 に答える