Posted at

jQueryのDeferredが便利過ぎた

More than 3 years have passed since last update.


はじめに

この記事はjQuery初心者がajaxの非同期処理に苦しみDeferredというものを使って解決した話を解説するものです。PHP, HTMLが出てきますが、そのあたりはひと通り理解できている人向けです。そこの解説はしません。


やること


  • サンプルボタンを押すと内部的にajax通信を行い値を取得


  • ajax通信が成功した場合にはDOMを生成

  • またクリック関数が終えたタイミングでもDOMを生成

  • 上記2つの順番が今回の焦点


Deferredを使わない場合のコード

配置

├── index.html
├── index.php
└── sample.js


index.html

<!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>



index.php

<?php

if(isset($_POST['test_text'])){
echo($_POST['test_text']);
}


sample.js

$(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');
});
}
});



実行結果

スクリーンショット 2015-05-28 0.12.54.png

下記はコンソールログ

スクリーンショット 2015-05-28 0.25.59.png

ajax関数内でアペンドしているものより先にクリック関数内でアペンドしているものが先にDOMに加わっている。コンソールログを見てみても、ajaxが終わる前にクリック関数が先に処理を最後まで終えてしまっている。

これを「ajaxの処理が終わってから次の処理をする」という風にしたい。

そこで出てくるのがDeferredである。


Deferredを使った場合

コード配置は先程と同様

index.htmlindex.phpも変更なし


sample.js

$(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オブジェクトが出てきているところがポイントです。


実行結果

スクリーンショット 2015-05-28 0.09.24.png

下記はコンソールログ

スクリーンショット 2015-05-28 0.33.50.png

ajax関数内でアペンドするのを待ってからクリック関数内のアペンドが呼び出されているのが分かる。


おわりに

ajaxの非同期処理は便利なのですが、稀に「この処理の後にこれがしたい」という順序依存が発生する場合があるので、そういう時には上記のようなテクニックを使えば実現できるかと思います。

Deferredというキーワードを覚えておきましょう。

ちなみにdeferとは「延ばす・延期する」といった意味だそうです。