今更のPromise再考

　[深くなるコールバックをイベントで少し解決する](http://qiita.com/zrelyydereva/items/82a20295f78e127cea06) に頂いたコメントの内容を理解するべく、読んだり書いたりしてみた。

# 超長いので先に結論
　食わず嫌いだった。
　IEさんでPolyfillなく動いてほしい。
　eventに関しては、eventとして動くべきものは、eventで動けばよい。
　なんとなく思いつく範囲では下記のようなもの。

- DOMへの通知、DOMから何か受けるところ。
- ダイアログ出す、とか。
- プローブあてるためのチェックピン的なイベント。
- N対1とか、1対Nとか、N対Nなもののハブ。

# 書きながら整理していった
　Promiseに関して、割と食わず嫌いだったので、よく考えてみた。
　書きながら、いろいろ整理。

## $.ajaxとか。
　こういう、Promiseそのものかどうかよくわかんないが、とりあえずthenできるものをthenableと言うそうな。

　とりあえず切り出す。

```js
var requestJson = function(request) {
    return Promise.resolve($.ajax(
        Object.assign({
            dataType: "json"
        }, request)
    ))
}
```

## エラーの扱い

　catchしなければ上位に上がっていく。
　普通に例外。なるほど便利。
　いったん闇雲に使ってみる。

```js
var verify_token = function(token) {
    return new Promise(function(resolve, reject) {
        //トークンがOK
        resolve(token);
        //なにがしかのエラー
        //reject(token);
    })
}
```

```js
var auth_by_token = new Promise(function(resolve, reject) {
    requestJson({
        url: './renew_token.php',
        data: {
            token: window.localStorage ? localStorage.getItem('token') : ""
        }
    }).then(verify_token).then(function(data) {
        resolve(data)
    });
    //rejectは沸いて上がる
});
```

画面周りは適当にやる

```js
$e.on('request_show_message', function(msg) {
    //DOMいじったり、フレームワークに渡したり。
    alert(msg);
});
var auth_error = function(error) {
    //エラー時の統率。
    localStorage.setItem('token', '');
    //イベント名がいびつだった。
    $e.trigger('request_show_message', 'ログインに失敗しました(' + error + ')');
    return;
};
```

#使ってみる

```js
Promise.resolve($.ready)
    .then(auth_by_token)
    .then($e.proxyone('token_refreshed'))
    .catch(auth_error);
```

なんとなくすでに楽だけど、全然役割分担できてない。これでは、ただイベントがPromiseになっただけ。
それでも例外のメリットはある。（なにがしかのエラー、を起こすとちゃんとハンドリングできる）

そのまま、ログインボタンを想定して書き進めてみた。

```js
$("#login").on('click', function() {
    //ログインするための情報を用意
    var username = $("#username").val();
    var password = $("#password").val();
    requestJson({
            url: './issue_token.php',
            data: {
                username: username,
                password: password
            }
        }).then(verify_token).then($e.proxyone('token_refreshed'))
        .catch(auth_error);
})
```

#なるほど、共通点が見えてきた。

こう書くと、扱いやすい。

```js
var auth_by_token = function() {
    return requestJson({
        url: './renew_token.php',
        data: {
            token: window.localStorage ? localStorage.getItem('token') : ""
        }
    });
};

var auth_by_password = function authByPassword(username, password) {
    return requestJson({
        url: './issue_token.php',
        data: {
            username: username,
            password: password
        }
    });
}
```

認証、を切り出す。

```js
var authAction = function authAction(method) {
    return new Promise(function(resolve, reject) {
        method.then(verify_token).then(resolve).catch(auth_error);
    });
}
```

使う。

```js
var authByToken = function() {
    return authAction(auth_by_token());
};

Promise.resolve($.ready)
    .then(authByToken)
    .then($e.proxyone('token_refreshed'));
```

ボタン

```js
$(document).ready(function() {
    $("#login").on('click', function() {
        var username = "aaaaa"; //$("#username").val();
        var password = "bbbbb"; //$("#password").val();
        authAction(auth_by_password(username, password))
            .then($e.proxyone('token_refreshed'));
    })
});
```

##なるほど。


# DB初期化のほう。

　いろいろ考えているうちに、思考が明後日のところに行った。
　最終的に、非同期処理はわりとどうでもよくて、同じ形を同じ形でつなげられることに気付く。
　***非同期処理を含む、(繰り返しのない)流れを制御するもの***が、騒ぎ出す。

　error,resultのイベントでのチェーンは車輪を再発明しようとして、スキー板作ってたな。
　スキーするときには便利だろうけど、滑り降りることしかできない。
　今まで、動くか潔くABENDするかどっちかを強いられてきたのでなんとなく自覚はしていた。

　例示していただいたloopDfdの、actorDfdが、loopを呼び出してるのがなるほど感があってヤバかった。

　自分が、こうなればいいのにな、って思っていたことを~~盗作~~実装してまとめる。

- ほぼ無限にも回せるし、一定回数であきらめることもできる。
- 待ちタスクが定義できる

```js
var sleep = function(ms) {
    var _ms = ms;
    return function _sleep() {
        return new Promise(function(resolve, reject) {
            setTimeout(resolve, _ms)
        });
    }
};

//taskなんとかはこんな感じ。
var count = 0;
var task2_of_xxxdb = function() {
    return new Promise(function(resolve, reject) {
        //５回断られる
        if (count++ < 5) {
            console.log('作業中2:' + count);
            return reject('作業中2');
        }
        return resolve('OK');
    });
};

var sequential_exec = function(arr_promises, delay_promise, max_retry) {
    var errmsg = 'reached to retry-limitations';
    return new Promise(function(resolve, reject) {
        ! function _sequential_exec(arr_promises, delay_promise, max_retry) {
            if (!arr_promises[0]) return resolve();
            if (max_retry == 0) return reject(errmsg);
            var t = [].concat(arr_promises); //cdr
            var current = t.shift();         //car
            current().then(function() {
                    _sequential_exec(t, delay_promise, max_retry)
                })
                .catch(function() {
                    _sequential_exec([delay_promise].concat(arr_promises), delay_promise, max_retry - 1)
                })
        }(arr_promises, delay_promise, max_retry);
    });
}

sequential_exec([init_xxxdb, task_of_xxxdb, task2_of_xxxdb], sleep(1000), 100) //100回まで頑張る。-1入れるとアンダーフローするまで回る。
    .then($e.proxyone('xxxdb_ok'))
    .catch($e.proxyone('xxxdb_error')); 
```