はじめに
この記事はjQuery
初心者がajax
の非同期処理に苦しみDeferred
というものを使って解決した話を解説するものです。PHP
, HTML
が出てきますが、そのあたりはひと通り理解できている人向けです。そこの解説はしません。
やること
- サンプルボタンを押すと内部的に
ajax
通信を行い値を取得 -
ajax
通信が成功した場合にはDOM
を生成 - またクリック関数が終えたタイミングでも
DOM
を生成 - 上記2つの順番が今回の焦点
Deferred
を使わない場合のコード
配置
├── index.html
├── index.php
└── sample.js
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js"></script>
</head>
<body>
<button id='sample'>SAMPLE</button>
<ul id='append_area'></ul>
<script type="text/javascript" src="sample.js"></script>
</body>
</html>
<?php
if(isset($_POST['test_text'])){
echo($_POST['test_text']);
}
$(function(){
//サンプルボタンがクリックされたら発火するイベント
$('#sample').on('click', function(){
//ajaxを使う関数を呼び出し、挙動がズレることを確認する
console.log('click function start');
ajax_function();
$('#append_area').append('<li>クリック関数側で追加したテキスト</li>');
console.log('click function end');
});
//ajax通信をし、返ってきた値をリストに追加する
function ajax_function(){
console.log('ajax start');
$.ajax({
type: 'POST',
url: 'index',
data: {
'test_text': 'ajax関数側で追加したテキスト'
}
}).done(function(data){
console.log('done');
console.log(data);
$('#append_area').append('<li>'+ data +'</li>');
}).fail(function(){
console.log('fail');
}).always(function(){
console.log('ajax finish');
});
}
});
実行結果
ajax
関数内でアペンドしているものより先にクリック関数内でアペンドしているものが先にDOM
に加わっている。コンソールログを見てみても、ajax
が終わる前にクリック関数が先に処理を最後まで終えてしまっている。
これを「ajax
の処理が終わってから次の処理をする」という風にしたい。
そこで出てくるのがDeferred
である。
Deferred
を使った場合
コード配置は先程と同様
index.html
とindex.php
も変更なし
$(function(){
//サンプルボタンがクリックされたら発火するイベント
$('#sample').on('click', function(){
console.log('click function start');
//ajax関数のリターン値としてDeferredオブジェクトを受け取る
var deferred = ajax_function();
//Deferredオブジェクトを監視し、完了の通知がきたらdone内を実行
deferred.done(function(){
$('#append_area').append('<li>クリック関数側で追加したテキスト</li>');
console.log('deferred done');
});
console.log('click function end');
});
//ajax通信をし、返ってきた値をリストに追加する
function ajax_function(){
console.log('ajax start');
//完了を知らせるためにDeferredオブジェクトを生成しそれを返す
var deferred = new $.Deferred();
$.ajax({
type: 'POST',
url: 'index',
data: {
'test_text': 'ajax関数側で追加したテキスト'
}
}).done(function(data){
console.log('done');
console.log(data);
$('#append_area').append('<li>'+ data +'</li>');
}).fail(function(){
console.log('fail');
}).always(function(){
console.log('ajax finish');
//ajax処理を終了したことをDeferredオブジェクトに通知
deferred.resolve();
});
//完了を知らせるためにDeferredオブジェクトを生成しそれを返す
return deferred;
}
});
各所にDeferred
オブジェクトが出てきているところがポイントです。
実行結果
ajax
関数内でアペンドするのを待ってからクリック関数内のアペンドが呼び出されているのが分かる。
おわりに
ajax
の非同期処理は便利なのですが、稀に「この処理の後にこれがしたい」という順序依存が発生する場合があるので、そういう時には上記のようなテクニックを使えば実現できるかと思います。
Deferred
というキーワードを覚えておきましょう。
ちなみにdefer
とは「延ばす・延期する」といった意味だそうです。