jQueryのDeferredが便利過ぎた

## はじめに

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

## やること

* サンプルボタンを押すと内部的に`ajax`通信を行い値を取得
* `ajax`通信が成功した場合には`DOM`を生成
* またクリック関数が終えたタイミングでも`DOM`を生成
* 上記2つの順番が今回の焦点

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

<pre>配置
├── index.html
├── index.php
└── sample.js
</pre>

```html: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>
```

```php:index.php
<?php
    if(isset($_POST['test_text'])){
        echo($_POST['test_text']);
    }
```

```js: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](https://qiita-image-store.s3.amazonaws.com/0/67471/b230da47-fba5-20f7-ce5c-d807aeffd2e9.png)
下記はコンソールログ
![スクリーンショット 2015-05-28 0.25.59.png](https://qiita-image-store.s3.amazonaws.com/0/67471/710ba987-69b3-efc2-430b-f3c6f1349c56.png)

`ajax`関数内でアペンドしているものより先にクリック関数内でアペンドしているものが先に`DOM`に加わっている。コンソールログを見てみても、`ajax`が終わる前にクリック関数が先に処理を最後まで終えてしまっている。
これを「`ajax`の処理が終わってから次の処理をする」という風にしたい。
そこで出てくるのが`Deferred`である。

## `Deferred`を使った場合

コード配置は先程と同様
`index.html`と`index.php`も変更なし

```js: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](https://qiita-image-store.s3.amazonaws.com/0/67471/20744017-1d31-8748-3566-1ca7a6629c84.png)

下記はコンソールログ
![スクリーンショット 2015-05-28 0.33.50.png](https://qiita-image-store.s3.amazonaws.com/0/67471/9d4e9afe-cb5b-eeb6-2c51-5d464285b8e6.png)

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

## おわりに

`ajax`の非同期処理は便利なのですが、稀に「この処理の後にこれがしたい」という順序依存が発生する場合があるので、そういう時には上記のようなテクニックを使えば実現できるかと思います。
`Deferred`というキーワードを覚えておきましょう。
ちなみに`defer`とは「延ばす・延期する」といった意味だそうです。