39

複数のDeferredオブジェクトがjQuery.whenに渡されると、メソッドは、渡されたすべてのDeferredの集約状態を追跡する新しい「マスター」DeferredオブジェクトからPromiseを返します。

方法はどちらかになります

  1. すべての遅延が解決されるとすぐにマスター遅延を解決する、または
  2. 延期の1つが拒​​否されるとすぐに、マスター延期を拒否します。

マスターDeferredが解決された場合(つまり、すべてのDeferredが解決された場合)、jQuery.whenに渡されたすべてのDeferredの解決された値が渡されます。たとえば、DeferredsがjQuery.ajax()リクエストの場合、引数は、引数リストで指定された順序で、リクエストのjqXHRオブジェクトになります。

$.when( $.getJSON('foo'), $.getJSON('bar') ).done(function(foo, bar) {

    // foo & bar are jqXHR objects for the requests

});

Deferredの1つが拒​​否された複数のDeferredの場合、jQuery.when IMMEDIATELY FIRESは、その時点で一部のDeferredがまだ解決されていない場合でも、マスターDeferredの失敗コールバックを実行します。

$.when( $.getJSON('foo'), $.getJSON('bar') ).fail(function(req) {

    // req is the jqXHR object for one of the failed requests

});

すべてのDeferredがjQuery.whenに渡されたときに、コールバックを起動する必要があります。200個のOKコードでJSONオブジェクトを送信し(代わりに404個のNot FoundエラーステータスコードでJSONを送信)、done()メソッドで成功/エラーを判断できますが、APIをRESTfulに保つことをお勧めします。どうすればこれを達成できますか?

4

8 に答える 8

45

Deferredこれを行う最も簡単な方法は、AJAXリクエストごとにセカンダリオブジェクトを保持し、常に解決されるようにすることだと思います。

var d1 = $.Deferred();
var d2 = $.Deferred();

var j1 = $.getJSON(...).complete(d1.resolve);
var j2 = $.getJSON(...).complete(d2.resolve);

$.when(j1, j2).done(function() {
     // only fires if j1 AND j2 are resolved
});

$.when(d1, d2).done(function() {
     // will fire when j1 AND j2 are both resolved OR rejected
     // check j1.isResolved() and j2.isResolved() to find which failed
});

これは、.complete()jQueryがAJAXメソッドのpromiseに追加する追加のAJAXメソッドを利用しています。これは、解決されたPromiseと拒否されたPromiseの両方に対して呼び出されます。

注意:それ自体がコールバックとして機能します。ブロックd1.resolveでラップする必要はありません。function() { ... }

于 2011-04-28T21:25:04.370 に答える
11

@Alnitakの回答は巧妙で、作成したハックを消去するのに役立ちました。そのハックでは、基になる結果に関係なく、約束を人為的に解決していました。これにより、「いつ」を使用して複数のリクエストをまとめて「完了」を使用できるようになりました成功/失敗に関係なく続行します。

任意の数の基本的な約束をサポートする彼の提案に別の用途を提供することを期待して、私はアルニタックの答えに「答えています」。

var asyncFunc, entity, entities, $deferred, $deferreds;
// ...
foreach (entity in entities) {
    $deferred = $.Deferred();
    $deferreds.push($deferred);
    asyncFunc(entity).done(...).fail(...).always($deferred.resolve);
}
// ...
$.when.apply($, $deferreds).done(...)

これは疑似 JavaScript ですが、アプローチを伝える必要があります。エンティティの任意のサイズのセットについては、エンティティごとに遅延 ($deferred) を作成し、それを配列 ($deferreds) にプッシュし、非同期呼び出しを行い、必要に応じて完了/失敗を追加しますが、常にこれを解決する「常に」を含めます。エンティティの $deferred。:「常に」は、呼び出しではなく、遅延の解決関数を受け取ります。

'when' は $deferreds 配列を 'when' の引数リストに変換します。この一連の deferreds は (always のおかげで) 解決されることが保証されているため、一度呼び出される 'done' を定義できるようになりました。これらの成功/失敗に関係なく、非同期呼び出しは完了します。

于 2012-08-02T21:02:40.263 に答える
10

最近、役立つプラグインを作成しました。私はそれを呼びます$.whenAll

この拡張機能は、すべての成功と失敗を進行イベントとして扱います。すべての promise が完了した後、エラーがなければグローバルな promise が解決されます。それ以外の場合、グローバルな約束は拒否されます。

$.whenAll - https://gist.github.com/4341799 (テスト)

使用例:

$.whenAll($.getJSON('foo'), $.getJSON('bar'))
  .then(
    doneCallback
    ,failcallback
    // progress callback
    // the only problem is $.ajax.done/fail states call their callbacks 
    // with params in different locations (except for state)
    ,function(data, state, jqXhr) {
      if (state == 'success') {
        // do happy stuff
      }
      else { // error (fail)
        // `data` is actually the jqXhr object for failed requests
        // `jqXhr` is the text of the error "Not Found" in this example
      }
    }
  )
;
于 2013-01-30T12:58:54.303 に答える
9

私の実装:

プラグイン コード:

jQuery.whenAll = function (deferreds) {
        var lastResolved = 0;

        var wrappedDeferreds = [];

        for (var i = 0; i < deferreds.length; i++) {
            wrappedDeferreds.push(jQuery.Deferred());

            deferreds[i].always(function() {
                wrappedDeferreds[lastResolved++].resolve(arguments);
            });
        }

        return jQuery.when.apply(jQuery, wrappedDeferreds).promise();
    };

使用するには:

jQuery.whenAll([jQuery.get('/your-resource'), jQuery.get('/your-resource')])
   .done(
       function(result1, result2) {
           console.log(result1[1]);
           console.log(result2[1]);
       });

フィドルをチェックしてください: http://jsfiddle.net/LeoJH/VMQ3F/

于 2014-02-26T19:08:05.200 に答える
3

$.when()これは、セマンティクスを使用するために実際のコア コードを変更して作成した jQuery プラグインです。より良い名前が必要なため、次のように呼ばれ$.myWhen()ます。

(function($) {
  $.myWhen = function( subordinate /* , ..., subordinateN */ ) {
    var i = 0,
      responseValues = Array.prototype.slice.call( arguments ),
      length = responseValues.length,

      // the count of uncompleted subordinates
      remaining = length !== 1 || ( subordinate && jQuery.isFunction( subordinate.promise ) ) ? length : 0,

      // the master Deferred. If responseValues consist of only a single Deferred, just use that.
      deferred = remaining === 1 ? subordinate : jQuery.Deferred(),

      // Update function for all resolve, reject and progress values
      updateFunc = function( i, contexts, values ) {
        return function( value ) {
          contexts[ i ] = this;
          values[ i ] = arguments.length > 1 ? Array.prototype.slice.call( arguments ) : value;
          if( values === progressValues ) {
            deferred.notifyWith( contexts, values );
          } else if ( !( --remaining ) ) {
            deferred.resolveWith( contexts, values );
          }
        };
      },

      progressValues, progressContexts, responseContexts;

    // add listeners to Deferred subordinates; treat others as resolved
    if ( length > 1 ) {
      progressValues = new Array( length );
      progressContexts = new Array( length );
      responseContexts = new Array( length );
      for ( ; i < length; i++ ) {
        if ( responseValues[ i ] && jQuery.isFunction( responseValues[ i ].promise ) ) {
          responseValues[ i ].promise()
            .always( updateFunc( i, responseContexts, responseValues ) )
            .progress( updateFunc( i, progressContexts, progressValues ) );
        } else {
          --remaining;
        }
      }
    }

    // if we're not waiting on anything, resolve the master
    if ( !remaining ) {
      deferred.resolveWith( responseContexts, responseValues );
    }

    return deferred.promise();
  };
})(jQuery);

jQuery をロードした場所の直後にこのコードを配置するだけで、$.myWhen()関数は と一緒に使用できるようになります$.when()。セマンティクスを除いて、他のすべては 100% まったく同じです。

于 2012-08-21T20:24:51.200 に答える
0

@Alnitak と @DazWilkin の回答は素晴らしいです! しかし、私は個人的に機能的なスタイルを好むので、ここに任意の数の約束のための機能的なバージョンがあります:

var entities;
// ...
var deferreds = entities.map(function() {
    var deferred = $.Deferred();
    asyncFunc(this).done(...).fail(...).always(deferred.resolve);
    return deferred;
}
// ...
$.when.apply($, deferreds).done(...)

@DazWilkinの回答と比較して、map代わりに関数を使用しますforeach

于 2015-06-15T21:31:29.420 に答える
0

when に 2 つのリクエストがあり、リクエストの 1 つが失敗した場合でも個々の成功にアクセスできるソリューションを見つけました。

        $.when
        (
            $.getJSON(...).then(function (results)
            {
                console.log('SUCCESS REQUEST 1 BY ITSELF', results);
            }),
            $.getJSON(...).then(function (results)
            {
                console.log('SUCCESS REQUEST 2 BY ITSELF', results);
            })
        ).then
        (
            function (results1, results2)
            {
                console.log('BOTH REQUESTS SUCCESSFUL...');
                console.log('results1', results1);
                console.log('results2', results2);
            },
            function (error1, error2)
            {
                console.log('AT LEAST 1 REQUEST FAILED...');
                console.log('error1', error1);
                console.log('error2', error2);                  
            }
        );
于 2016-01-26T16:40:46.180 に答える
0

サーバーからリソースをフェッチするだけではない、より一般的なユース ケースに対する Leo Hernandez のソリューションの改善。たとえば、ユーザー インタラクションによってトリガーされるイベントや、非同期の jQuery UI 呼び出し (slideUp() および slideDown()) を含めることができます。拡張された使用例については、 https://jsfiddle.net/1trucdn3/を参照してください。

$.whenAll = function (deferreds) {
    var lastResolved = 0;
    var wrappedDeferreds = [];

    for (var i = 0; i < deferreds.length; i++) {
        wrappedDeferreds.push($.Deferred());
        if (deferreds[i] && deferreds[i].always) {
            deferreds[i].always(wrappedDeferreds[lastResolved++].resolve);
        } else {
            wrappedDeferreds[lastResolved++].resolve(deferreds[i]);
        }
    }

    return $.when.apply($, wrappedDeferreds).promise();
};

この改善により、Deferred 以外の値を配列引数に渡すことができます。これは $.when() でできることでした。また、ステータスに関係なく結果を取得したい場合に備えて、元の $.when() メソッドの動作に合わせて、コールバック関数で取得する出力をクリーンアップしました。Leo のソリューションは、遅延オブジェクト全体を結果として渡すため、必要な情報を見つけるために掘り下げる必要があります。

$.whenAll([1, $.Deferred().resolve("Good"), $.Deferred().reject("Bad")])
    .done(function (result1, result2, result3) {
        // result1 -> 1
        // result2 -> "Good"
        // result3 -> "Bad"
    });
于 2015-05-21T18:00:55.923 に答える