LoginSignup
0
0

More than 5 years have passed since last update.

今更のPromise再考

Posted at

 深くなるコールバックをイベントで少し解決する に頂いたコメントの内容を理解するべく、読んだり書いたりしてみた。

超長いので先に結論

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

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

書きながら整理していった

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

$.ajaxとか。

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

 とりあえず切り出す。

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

エラーの扱い

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

var verify_token = function(token) {
    return new Promise(function(resolve, reject) {
        //トークンがOK
        resolve(token);
        //なにがしかのエラー
        //reject(token);
    })
}
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は沸いて上がる
});

画面周りは適当にやる

$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;
};

使ってみる

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

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

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

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

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

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

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
        }
    });
}

認証、を切り出す。

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

使う。

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

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

ボタン

$(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を呼び出してるのがなるほど感があってヤバかった。

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

  • ほぼ無限にも回せるし、一定回数であきらめることもできる。
  • 待ちタスクが定義できる
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) {
        //5回断られる
        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')); 
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0