jQuery.Deferred()
を使うと、非同期処理を、コールバック地獄に陥ることなく、いろいろな方法でつなぐことができる。複数の非同期処理を直列や並列でつなぐことはもちろん、条件によって特定の処理をスキップしたり定期的に繰り返したりもできる。
参考:
jQuery API Documentation
プロミスの使用 @ MDN
基本的な例
今更ながら、基本的な使い方として、 then()
で、複数の非同期処理を直列につなぐことができる。
// 非同期で url に request を送り response を受け取る関数。
// Promise を返すことにより、then() でつなげられる。
const async_func = function(url, request) {
const deferred = $.Deferred();
// ...
if ( 成功 ) {
deferred.resolve(response);
} else {
deferred.reject(response);
}
return deferred.promise();
};
// まずは async_func(url1, request1) を実行
async_func(url1, request1).then(
// response1 が返ってきたら
// async_func(url2, request2) を実行
function(response1) {
// ...
return async_func(url2, request2);
}
).then(
// response2 が返ってきたらこれを実行
function(response2) { ... }
).fail(
// いずれかの async_func() が失敗したらこれを実行
function(responce) { ... }
);
jQuery.ajax() を使う例
jQuery.ajax()
が返す jqXHR
は Deferred
を継承しているので、それを返すだけで then()
でつなげられる。
$.ajax(url1).then(
// $.ajax(url1) が成功した場合
function(response1) {
// ...
return $.ajax(url2);
}
).then(
// $.ajax(url2) が成功した場合
function(response2) { ... }
).fail(
// いずれかの $.ajax() が失敗した場合
function(jqXHR, textStatus, errorThrown) { ... }
);
条件によって処理をスキップする例
jQuery.when()
に Deferred
でも Promise
でもない引数を 1 つ渡すと(または引数を渡さないと)、resolve した Promise
を返す。
$.get(url1).then(
function(response1) {
if ( スキップ ) {
// $.get(url2) をスキップして $.get(url3) を実行
return $.when(response1);
} else {
// $.get(url2) を実行してから $.get(url3) を実行
return $.get(url2);
}
}
).then(
// $.get(url2) をスキップした場合は response1 === response2
function(response2) {
return $.get(url3);
}
).then(
function(response3) { ... }
).fail(
function(jqXHR, textStatus, errorThrown) { ... }
);
複数の非同期処理を並列で行う例
これが jQuery.when()
の本来の使い方。
$.when($.get(url1), $.get(url2)).then(
// $.get(url1) と $.get(url2) の両方が成功した場合
function(resolved1, resolved2) {
// 各 resolved は [data, statusText, jqXHR]
}
).fail(
// $.get(url1) と $.get(url2) のいずれかが失敗した場合
function(jqXHR, textStatus, errorThrown) { ... }
);
進捗を定期的に確認する例
サーバーで時間のかかる処理を行うための、以下のような API があるとする。
- "jobs/post" にデータを post するとジョブが作成され開始される。ジョブのデータが返る。
- "jobs/get/<jobID>" に get するとジョブのデータが返る。status は "success", "in_progress", "fail" のいずれか。
// 定期的にジョブのデータを取得する関数
const get_job = function(url, deferred, interval) {
setTimeout(function() {
$.get(url).done(
function(resonse) {
if ( response.status === "in_progress" ) {
// 処理中の場合、自分自身を再起呼び出し
get_job(url, deferred, interval);
} else {
// 処理が終わっていたら resolve
deferred.resolve(response);
}
}
).fail(
// 問い合わせに失敗したら reject
function(jqXHR, textStatus, errorThrown) {
deferred.reject([jqXHR, textStatus, errorThrown]);
}
);
}, interval);
};
$.post("jobs/post", params).then(
// 1秒おきに進捗確認
function(response) {
const deferred = $.Deferred();
get_job("jobs/get/"+response.id, deferred, 1000);
return deferred.promise();
}
).then(
function(response) {
if ( response.status === "success" ) {
// 処理成功
} else {
// 処理失敗
}
}
).fail(
function(jqXHR, textStatus, errorThrown) { ... }
);