完了するためにajaxが必要な3つのプロセスがあります。しかし、それは非同期であり、私がやりたかったことを実行できません。
まあ言ってみれば:
function a(param1, param2) {
$.post(..., function(result){
if(result){
b();
} else {
console.log("failed a");
}
})
}
function b() {
$.post(..., function(result){
if(result){
c();
} else {
console.log("failed b");
}
})
}
function c() {
$.post(..., function(result){
if(result){
console.log("successful");
} else {
console.log("failed b");
}
})
}
このように実行してほしい
a
b
c
ご覧のとおり、そのコードは完全に機能しますが、ループを使用する場合。
var data = [{param1 : 1235, param2: 3214}, {param1 : 5432, param2: 9876}];
$.each(data, function(k,v){
a(v.param1, v.param2)
});
期待どおりに機能せず、次のようになります。
a
a
b
b
c
c
の代わりに
a
b
c
a
b
c
このようなことを書く方法はたくさんあります。
柔軟なアプローチは、「アクション」と「シーケンス」を分離することで、次のことを可能にします。
チェーンロジック専用に.then()
を使用して、このアプローチをコーディングする方法は次のとおりです。
function a() {
return $.post(...).then(function(result) {
if(result)
return result;//continue on "success" path.
else
return $.Deferred().reject('a').promise();//convert success to failure.
}, function() {
return 'a';//continue on failure path.
});
}
function b() {
return $.post(...).then(function(result) {
if(result)
return result;//continue on "success" path.
else
return $.Deferred().reject('b').promise();//convert success to failure.
}, function() {
return 'b';//continue on failure path.
});
}
function c() {
return $.post(...).then(function(result) {
if(result)
return result;//continue on "success" path.
else
return $.Deferred().reject('c').promise();//convert success to failure.
}, function() {
return 'c';//continue on failure path.
});
}
a().then(b).then(c).then(function() {
console.log("successful");
}, function(id) {
console.log("failed: " + id);
});
または、ループ内から呼び出される単一の非同期関数a
が必要な場合、コードは次のようになります。
function a(obj) {
return $.post(...).then(function(result) {
if(result)
return result;//continue on "success" path.
else
return $.Deferred().reject(obj.id).promise();//convert success to failure.
}, function() {
return obj.id;//continue on failure path.
});
}
var data = [{id:'A', param1:1235, param2:3214}, {id:'B', param1:5432, param2:9876}];
//Note how IDs are included so these data objects can be identified later in failure cases.
var dfrd = $.Deferred();//starter Deferred for later resolution.
var p = dfrd.promise();//A promise derived from the starter Deferred, forming the basis of a .then() chain.
//Build a .then() chain by assignment
$.each(data, function(i, obj) {
p = p.then( function() {
return a(obj);
});//By not including a fail handler here, failures will pass straight through to be handled by the terminal .then()'s fail handler.
});
//Chain a terminal .then(), with success and fail handlers.
p.then(function() {
console.log("successful");
}, function(id) {
console.log("failed: " + id);
});
dfrd.resolve();//Resolve the starter Deferred to get things started.
JQueryの据え置きオブジェクトと「then」を使用して、ajax呼び出しのような非同期呼び出しを連鎖させることができます。
私の例のようにajax呼び出しの代わりに、遅延promiseオブジェクトを返す関数を使用するように変更することもできます。
(非同期関数の例: http://jsfiddle.net/q4cFv/1/ )
$(function() {
var delay = 3,
span = $('span'),
posts = [
{
input1: 'My name 1',
input2: 'My address 1',
input3: 'My country 1'
},
{
input1: 'My name 2',
input2: 'My address 2',
input3: 'My country 2'
},
{
input1: 'My name 3',
input2: 'My address 3',
input3: 'My country 3'
},
{
input1: 'My name 4',
input2: 'My address 4',
input3: 'My country 4'
}
],
looper = $.Deferred().resolve();
$.each(posts, function(i, data) {
looper = looper.then(function() {
return $.ajax({
data: {
json: JSON.stringify(data),
delay: delay
},
method: 'post',
url: '/echo/json/',
dataType: 'json'
}).done(function(response) {
span.append('Response:<br />');
for(key in response) {
span.append(key + ': ' + response[key] + '<br />');
}
$('span').append('Waiting ' + delay + ' seconds<br /><br />');
});
});
});
});
cはbの結果に依存せず、baの結果に依存しません。
GRASPの原則に従う( http://en.wikipedia.org/wiki/GRASP_(object-directiond_design) 、aは知らないbとbはcを知らないはずです。
プログラムするときは、GRASPの原則またはガイドラインを覚えておくことが非常に重要です。
高凝集度と低結合度は、コードがより良く、より再利用可能で、保守が容易になることを意味します。
a、b、およびcを知っている主な関数は、連鎖呼び出し。
関数は次のようになります。
function a(param1, param2) {
var deferred = $.Deferred();
console.log(" function a: begin. Params " + param1 + " and " + param2);
mockPost("a_url").done(function() {
console.log(" function a: end ok. Params " + param1 + " and " + param2);
deferred.resolve();
}).fail(function() {
console.log(" function a: end fail. Params " + param1 + " and " + param2);
deferred.reject();
});
return deferred.promise();
}
function b() {
var deferred = $.Deferred();
console.log(" function b: begin");
mockPost("b_url").done(function() {
console.log(" function b: end ok.");
deferred.resolve();
}).fail(function() {
console.log(" function b: end fail.");
deferred.reject();
});
return deferred.promise();
}
function c() {
// We suppose that c function calls to post function and anything more
return mockPost("c_url");
}
主な機能は次のとおりです。
// Array with params for a function (a function is the first link in chain)
var data = [{param1 : 1235, param2: 3214}, {param1 : 5432, param2: 9876}];
// Array with calls to each fixed sequence a, b, and c. We iterate over data array
var arrayFunctions = [];
$.each(data, function(i,obj) {
arrayFunctions.Push(
function() {
console.log("Params in data with index " + i + ":");
// We define the fixed sequence: a with params, b without params and c without params
return $.iterativeWhen(
function() {
return a(obj.param1, obj.param2);
},
b,
c
);
}
)
});
// Start the global process
$.iterativeWhen.apply($, arrayFunctions)
.done(function() {
console.log ("----------------");
console.log ("> Global Success");
})
.fail(function() {
console.log ("--------------");
console.log ("> Global Fail");
});
$ .iterativeWhenはjQueryに存在しないので、私はそれを構築しました。 jQuery1.8以降のバージョンで動作します。
$.iterativeWhen = function () {
var deferred = $.Deferred();
var promise = deferred.promise();
$.each(arguments, function(i, obj) {
promise = promise.then(function() {
return obj();
});
});
deferred.resolve();
return promise;
};
MockPost関数は、成功確率で$ .postへの呼び出しをシミュレートします。
function mockPost(url) {
var deferred = $.Deferred();
setTimeout(function() {
if (Math.random() <= 0.9) {
console.log(" request url: " + url + "... ok");
deferred.resolve();
} else {
console.log(" request url: " + url + "... fail");
deferred.reject();
}
}, 1000);
return deferred.promise();
}
ログ出力は次のとおりです。
Params in data with index 0:
function a: begin. Params 1235 and 3214
request url: a_url... ok
function a: end ok. Params 1235 and 3214
function b: begin
request url: b_url... ok
function b: end ok.
request url: c_url... ok
Params in data with index 1:
function a: begin. Params 5432 and 9876
request url: a_url... ok
function a: end ok. Params 5432 and 9876
function b: begin
request url: b_url... ok
function b: end ok.
request url: c_url... ok
----------------
> Global Success
ここにjsFiddle: http://jsfiddle.net/E2tp3/
これは素晴らしくシンプルで効果の高いAJAXチェーン/キュープラグインです。ajaxメソッドを順番に実行します。
メソッドの配列を受け入れ、それらを順番に実行することで機能します。応答を待っている間は、次のメソッドを実行しません。
// ---この部分ISあなたのコード-----------------------
$(document).ready(function(){
var AjaxQ = []; AjaxQ[0] = function () { AjaxMethod1(); } AjaxQ[1] = function () { AjaxMethod2(); } AjaxQ[3] = function () { AjaxMethod3(); } //Execute methods in sequence $(document).sc_ExecuteAjaxQ({ fx: AjaxQ });
});
// ---この部分IS THE AJAX PLUGIN ----------------- -
$ .fn.sc_ExecuteAjaxQ =関数(オプション){
//? Executes a series of AJAX methods in dequence var options = $.extend({ fx: [] //function1 () { }, function2 () { }, function3 () { } }, options); if (options.fx.length > 0) { var i = 0; $(this).unbind('ajaxComplete'); $(this).ajaxComplete(function () { i++; if (i < options.fx.length && (typeof options.fx[i] == "function")) { options.fx[i](); } else { $(this).unbind('ajaxComplete'); } }); //Execute first item in queue if (typeof options.fx[i] == "function") { options.fx[i](); } else { $(this).unbind('ajaxComplete'); } }
}
問題は、すべてのa
を一度に呼び出しているが、最初のサイクルを待ってから次のサイクルに進むことです。次のサイクルを開始する前に、前の「a」サイクルが終了するのを待ちます。
A、b、cがコールバックを受け入れ、それを渡すと仮定しましょう。
次のようになります
function a(param1, param2,callback) {
$.post(..., function(result){
if(result){
b(callback);
} else {
console.log("failed a");
}
})
}
bは次のようになります:
function b(callback) {
$.post(..., function(result){
if(result){
c(callback);
} else {
console.log("failed b");
}
})
}
そしてcは次のようになります:
function c(callback) {
$.post(..., function(result){
if(result){
console.log("successful");
} else {
console.log("failed b");
}
callback();
})
}
これにより、サイクルがいつ完了するかがわかります。これで次のように書くことができます。
var data = [{param1 : 1235, param2: 3214}, {param1 : 5432, param2: 9876}];
var index = 0;
(function updateData(){
a(data[index].param1,data[index].param2,function(){ //call a with the data
index++;//update the index
updateData(); // start the next cycle
});
});