jQueryのpromiseの使い方のメモ

# jQueryの`promise`のお勉強

## `promise`
`promise`オブジェクトは、`deferred`から生成されます。
`deferred`から特定のAPI(`resolve`/`reject`など)を通すことで、
不正な状態操作をできないようにしています。

## `deferred`
`deferred`は、誤解を恐れずに単純化して言えば

- 内部にステートマシンを持ち、初期状態はpending
- `resolve()`されると、resolved状態になり、`done()`で登録されたコールバックをキック
- `reject()`されると、rejected状態になり、`fail()`で登録されたコールバックをキック

だけのオブジェクトです。

##thenが強力
特筆すべきは `then(done_func, fail_func)` というメソッドが強力で
こいつを利用すると、
時間がかかる処理(非同期処理)の順列が見た目きれいに書けたり、
`try ... catch` 的なことができたりするのです。

##使い方の勉強
そもそも `promise` ってなんで必要なの？どう使うの？はおいておいて、
下記では、`then`を使ったコーディングのお勉強...

**「3秒以内にボタンを押せば成功」**的な振る舞いの実装。

### 1. これはサンプルからもらってきたコードほぼそのまま

```js
    $(function () {
        var myDefer = $.Deferred(); // deferオブジェクト生成

        $("button").click(function(){  // 成功に倒れる条件設定
            myDefer.resolve();
        });

        setTimeout(function(){  // 失敗に倒れる条件設定
            myDefer.reject();
        },3000);

        var myPromise = myDefer.promise();  // promise取得(お約束)
        
        myPromise.then(
            function(){
                alert("DONE!");  // 成功なら DONE! を表示
            },
            function(){
                alert("FAIL!");  // 失敗なら FAIL! を表示
            }
        );
    });
```
### 2. `then`でチェーンしてたくさんつなげてみた

```js
    $(function () {
        var myDefer = $.Deferred();
        $("button").click(function(){
            myDefer.resolve();
        });
        setTimeout(function(){
            myDefer.reject();
        },3000);

        var myPromise = myDefer.promise();

		// thenはpromiseオブジェクトを返すのでチェーンができる
		// done/failのfunctionにただの関数を書くと、
		// 一番最初のpromise(ここだと myPromise)の成功/失敗が引き継がれる
		
        myPromise.then(
            function(){
                alert("DONE! 1");
            },
            function(){
                alert("FAIL! 1");
            }
        ).then(
            function(){
                alert("DONE! 2");
            },
            function(){
                alert("FAIL! 2");
            }
        ).then(
            function(){
                alert("DONE! 3");
            },
            function(){
                alert("FAIL! 3");
            }
        ).then(
            function(){
                alert("DONE! 4");
            },
            function(){
                alert("FAIL! 4");
            }
        );

    });
```

### 3. クロージャ (クラスっぽく)

```js
    $(function () {

        // promise factory
        function createPromise() {
            var myDefer = $.Deferred();
            $("button").click(function(){
                myDefer.resolve();
            });
            setTimeout(function(){
                myDefer.reject();
            },3000);
            return myDefer.promise();
        }

        createPromise().then(
            function(){
                alert("DONE! 1");
            },
            function(){
                alert("FAIL! 1");
            }
        ).then(
            function(){
                alert("DONE! 2");
            },
            function(){
                alert("FAIL! 2");
            }
        ).then(
            function(){
                alert("DONE! 3");
            },
            function(){
                alert("FAIL! 3");
            }
        ).then(
            function(){
                alert("DONE! 4");
            },
            function(){
                alert("FAIL! 4");
            }
        );

    });
```

### 4. `done`/`fail`関数の中で、`promise`生成してみる。

```js
    $(function () {

        // promise factory
        function createPromise() {
            var myDefer = $.Deferred();
            $("button").click(function(){
                myDefer.resolve();
            });
            setTimeout(function(){
                myDefer.reject();
            },3000);
            return myDefer.promise();
        }

		// done/failのコールバック関数の中で、
		// promiseを生成してreturnすると、
		// 次の then (成功/失敗の判断) は、
		// その生成された promise の結果で決まる。
		// これで、逐次処理が書ける。
		
        createPromise().then(
            function(){
                alert("DONE! 1");
                return createPromise(); // create new promise
            },
            function(){
                alert("FAIL! 1");
                return createPromise(); // create new promise
            }
        ).then(
            function(){
                alert("DONE! 2");
                return createPromise(); // create new promise
            },
            function(){
                alert("FAIL! 2");
                return createPromise(); // create new promise
            }
        ).then(
            function(){
                alert("DONE! 3");
            },
            function(){
                alert("FAIL! 3");
            }
        );

    });
```

### 5. `try ... catch` 的な

```js
    $(function () {

        // promise factory
        function createPromise(text) {
            // set text if specified.
            if (null != text) {
                $("button").text(text);
            }

            // create promise.
            var myDefer = $.Deferred();
            $("button").click(function(){
                myDefer.resolve();
            });
            setTimeout(function(){
                myDefer.reject();
            },3000);
            return myDefer.promise();
        }

		// thenのfail側を書かないと、
		// 何も実行せずに、次の then に移る。
		// その際には、2. で試したように、
		// 前の promise の状態を引き継ぐので、
		// たとえばこの例では、一度 fail に倒れると
		// ダダダッと処理が進んで、
		// 最後の then の fail 側が実行されることになる。
		
        // combo game.
        createPromise("click here in 3sec!").then(
            function() {
                alert("OK!");
                return createPromise("once again!!"); // create new promise
            }
        ).then(
            function() {
                alert("OK - 2 hit combo.");
                return createPromise("again quick!!"); // create new promise
            }
        ).then(
            function() {
                alert("Great! - 3 hit combo.");
                return createPromise("again again! last one!"); // create new promise
            }
        ).then(
            function() {
                alert("Perfect! - 4 hit combo.");
            },
            function() {
                alert("failed to make combo.");
            }
        );

    });
```

以上です。