web-dev-qa-db-ja.com

Deferredの配列を$ .when()に渡します。

これが何が起こっているのかを考えた例です。 http://jsfiddle.net/adamjford/YNGcm/20/

HTML:

<a href="#">Click me!</a>
<div></div>

JavaScript:

function getSomeDeferredStuff() {
    var deferreds = [];

    var i = 1;
    for (i = 1; i <= 10; i++) {
        var count = i;

        deferreds.Push(
        $.post('/echo/html/', {
            html: "<p>Task #" + count + " complete.",
            delay: count
        }).success(function(data) {
            $("div").append(data);
        }));
    }

    return deferreds;
}

$(function() {
    $("a").click(function() {
        var deferreds = getSomeDeferredStuff();

        $.when(deferreds).done(function() {
            $("div").append("<p>All done!</p>");
        });
    });
});

「すべて完了しました」が欲しいのです。すべての据え置きタスクが完了した後に表示されますが、$.when()は据え置きオブジェクトの配列を処理する方法を知っているようには見えません。 "全部できた!"配列はDeferredオブジェクトではないため最初に発生します。したがってjQueryは先に進み、それが完了したと見なします。

$.when(deferred1, deferred2, ..., deferredX)のようにオブジェクトを関数に渡すことができることはわかっていますが、解決しようとしている実際の問題において、実行時に遅延オブジェクトがいくつあるかは不明です。

420
adamjford

値の配列をany関数に渡して、通常それらが別々のパラメータであると想定している場合は、Function.prototype.applyを使用します。

$.when.apply($, my_array).then( ___ );

http://jsfiddle.net/YNGcm/21/ を参照してください。

ES6では、代わりに...拡散演算子 を使用できます。

$.when(...my_array).then( ___ );

どちらの場合も、.thenハンドラーが必要とする仮パラメーターの数を事前に知っていることはあまりないため、各約束事の結果を取得するには、そのハンドラーがarguments配列を処理する必要があります。

697
Alnitak

JQueryは配列ではなく個々のパラメータでresolve()done()コールバックを呼び出すので、上記の回避策(ありがとう)は、遅延オブジェクトのfail()メソッドに提供されたオブジェクトを取り戻すという問題に適切に対処しません。つまり、arguments疑似配列を使用して、解決されたオブジェクトまたは拒否されたオブジェクトをすべて遅延オブジェクトの配列で返す必要があります。これは見苦しいです。

$.when.apply($,deferreds).then(function() {
     var objects=arguments; // The array of resolved objects as a pseudo-array
     ...
};

遅延の配列を渡したので、結果の配列を取り戻すのはいいでしょう。 Array.sort()のようなメソッドを使用できるように、擬似配列ではなく実際の配列を取り戻すのもいいでしょう。

これらの問題に対処するwhen.jswhen.all()メソッドに着想を得たソリューションは次のとおりです。

// Put somewhere in your scripting environment
if (typeof jQuery.when.all === 'undefined') {
    jQuery.when.all = function (deferreds) {
        return $.Deferred(function (def) {
            $.when.apply(jQuery, deferreds).then(
                function () {
                    def.resolveWith(this, [Array.prototype.slice.call(arguments)]);
                },
                function () {
                    def.rejectWith(this, [Array.prototype.slice.call(arguments)]);
                });
        });
    }
}

次のように、コールバックで、遅延/約束の配列を渡して、解決済み/拒否されたオブジェクトの配列を返すことができます。

$.when.all(deferreds).then(function(objects) {
    console.log("Resolved objects:", objects);
});
102
crispyduck

配列にwhenメソッドを適用することができます。

var arr = [ /* Deferred objects */ ];

$.when.apply($, arr);

どのようにしてjQuery Deferredの配列を操作しますか?

38
Eli

複数の並列AJAX呼び出しを呼び出すときは、それぞれの応答を処理するための2つのオプションがあります。

  1. 同期AJAX call /を次々に使用/非推奨
  2. Promises'配列と $.whenpromisesを受け入れ、そのコールバック.doneはすべてのpromiseがそれぞれの応答で正常に返されたときに呼び出されます。

function ajaxRequest(capitalCity) {
   return $.ajax({
        url: 'https://restcountries.eu/rest/v1/capital/'+capitalCity,
        success: function(response) {
        },
        error: function(response) {
          console.log("Error")
        }
    });
}
$(function(){
   var capitalCities = ['Delhi', 'Beijing', 'Washington', 'Tokyo', 'London'];
   $('#capitals').text(capitalCities);

   function getCountryCapitals(){ //do multiple parallel ajax requests
      var promises = [];   
      for(var i=0,l=capitalCities.length; i<l; i++){
            var promise = ajaxRequest(capitalCities[i]);
            promises.Push(promise);
      }
  
      $.when.apply($, promises)
        .done(fillCountryCapitals);
   }
  
   function fillCountryCapitals(){
        var countries = [];
        var responses = arguments;
        for(i in responses){
            console.dir(responses[i]);
            countries.Push(responses[i][0][0].nativeName)
        }  
        $('#countries').text(countries);
   }
  
   getCountryCapitals()
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<div>
  <h4>Capital Cities : </h4> <span id="capitals"></span>
  <h4>Respective Country's Native Names : </h4> <span id="countries"></span>
</div>
7
vinayakj

$.when.applyarrayを必要としない単純な代替手段として、次のパターンを使用して複数の並列約束に対して単一の約束を生成できます。

promise = $.when(promise, anotherPromise);

例えば.

function GetSomeDeferredStuff() {
    // Start with an empty resolved promise (or undefined does the same!)
    var promise;
    var i = 1;
    for (i = 1; i <= 5; i++) {
        var count = i;

        promise = $.when(promise,
        $.ajax({
            type: "POST",
            url: '/echo/html/',
            data: {
                html: "<p>Task #" + count + " complete.",
                delay: count / 2
            },
            success: function (data) {
                $("div").append(data);
            }
        }));
    }
    return promise;
}

$(function () {
    $("a").click(function () {
        var promise = GetSomeDeferredStuff();
        promise.then(function () {
            $("div").append("<p>All done!</p>");
        });
    });
});

ノート:

  • promise = promise.then(newpromise)を使用して、誰かのチェーンが順番に約束しているのを見た後、私はこれを考え出しました。
  • 欠点は、それが舞台裏で追加のpromiseオブジェクトを作成し、最後に渡されるパラメータは(それらが追加のオブジェクトの中にネストされているので)あまり役に立ちません。それは短くてシンプルですがあなたが望むもののために。
  • 利点は、アレイやアレイ管理が不要なことです。
5
Gone Coding

$ .eachを使って他のものを提案したいです。

  1. 私たちはajax関数を次のように宣言します。

    function ajaxFn(someData) {
        this.someData = someData;
        var that = this;
        return function () {
            var promise = $.Deferred();
            $.ajax({
                method: "POST",
                url: "url",
                data: that.someData,
                success: function(data) {
                    promise.resolve(data);
                },
                error: function(data) {
                    promise.reject(data);
                }
            })
            return promise;
        }
    }
    
  2. Ajaxを使って送信する一連の関数を作成するコードの一部:

    var arrayOfFn = [];
    for (var i = 0; i < someDataArray.length; i++) {
        var ajaxFnForArray = new ajaxFn(someDataArray[i]);
        arrayOfFn.Push(ajaxFnForArray);
    }
    
  3. そしてajaxを送信しながら関数を呼び出す:

    $.when(
        $.each(arrayOfFn, function(index, value) {
            value.call()
        })
    ).then(function() {
            alert("Cheer!");
        }
    )
    
4

変換してES6にアクセスできる場合は、オブジェクトの各反復可能項目を個別の引数として適用するスプレッド構文を使用できます。これは$.when()が必要とする方法と同じです。

$.when(...deferreds).done(() => {
    // do stuff
});

MDNリンク - スプレッド構文

1
relic

私は各ループに投稿して、それからajaxから受け取った数字からいくつかのフィールドにhtmlマークアップを設定していたのとよく似たケースがありました。次に、これらのフィールドの(現在更新されている)値の合計を計算し、合計フィールドに入れる必要がありました。

したがって、問題は、私がすべての数字の合計を計算しようとしていたが、非同期のAjax呼び出しからまだデータが戻ってこなかったことでした。コードを再利用できるようにするには、いくつかの関数でこの機能を完成させる必要がありました。私の外部関数はデータを待ってから完全に更新されたDOMを使って何かをします。

    // 1st
    function Outer() {
        var deferreds = GetAllData();

        $.when.apply($, deferreds).done(function () {
            // now you can do whatever you want with the updated page
        });
    }

    // 2nd
    function GetAllData() {
        var deferreds = [];
        $('.calculatedField').each(function (data) {
            deferreds.Push(GetIndividualData($(this)));
        });
        return deferreds;
    }

    // 3rd
    function GetIndividualData(item) {
        var def = new $.Deferred();
        $.post('@Url.Action("GetData")', function (data) {
            item.html(data.valueFromAjax);
            def.resolve(data);
        });
        return def;
    }
0
Cameron Forward

AngularJSまたはQ promiseライブラリの変種を使用している場合は、この問題を解決する.all()メソッドがあります。

var savePromises = [];
angular.forEach(models, function(model){
  savePromises.Push(
    model.saveToServer()
  )
});

$q.all(savePromises).then(
  function success(results){...},
  function failed(results){...}
);

完全なAPIを参照してください。

https://github.com/kriskowal/q/wiki/API-Reference#promiseall

https://docs.angularjs.org/api/ng/service/$q

0
mastaBlasta