これが何が起こっているのかを考えた例です。 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)
のようにオブジェクトを関数に渡すことができることはわかっていますが、解決しようとしている実際の問題において、実行時に遅延オブジェクトがいくつあるかは不明です。
値の配列をany関数に渡して、通常それらが別々のパラメータであると想定している場合は、Function.prototype.apply
を使用します。
$.when.apply($, my_array).then( ___ );
http://jsfiddle.net/YNGcm/21/ を参照してください。
ES6では、代わりに...
拡散演算子 を使用できます。
$.when(...my_array).then( ___ );
どちらの場合も、.then
ハンドラーが必要とする仮パラメーターの数を事前に知っていることはあまりないため、各約束事の結果を取得するには、そのハンドラーがarguments
配列を処理する必要があります。
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.jsのwhen.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);
});
配列にwhen
メソッドを適用することができます。
var arr = [ /* Deferred objects */ ];
$.when.apply($, arr);
複数の並列AJAX呼び出しを呼び出すときは、それぞれの応答を処理するための2つのオプションがあります。
Promises'
配列と $.when
promise
sを受け入れ、そのコールバック.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>
$.when.apply
やarray
を必要としない単純な代替手段として、次のパターンを使用して複数の並列約束に対して単一の約束を生成できます。
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)
を使用して、誰かのチェーンが順番に約束しているのを見た後、私はこれを考え出しました。$ .eachを使って他のものを提案したいです。
私たちは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;
}
}
Ajaxを使って送信する一連の関数を作成するコードの一部:
var arrayOfFn = [];
for (var i = 0; i < someDataArray.length; i++) {
var ajaxFnForArray = new ajaxFn(someDataArray[i]);
arrayOfFn.Push(ajaxFnForArray);
}
そしてajaxを送信しながら関数を呼び出す:
$.when(
$.each(arrayOfFn, function(index, value) {
value.call()
})
).then(function() {
alert("Cheer!");
}
)
変換してES6にアクセスできる場合は、オブジェクトの各反復可能項目を個別の引数として適用するスプレッド構文を使用できます。これは$.when()
が必要とする方法と同じです。
$.when(...deferreds).done(() => {
// do stuff
});
私は各ループに投稿して、それから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;
}
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