JavaScript
jQuery

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. これはサンプルからもらってきたコードほぼそのまま

    $(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でチェーンしてたくさんつなげてみた

    $(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. クロージャ (クラスっぽく)

    $(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生成してみる。

    $(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 的な

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

    });

以上です。