深くなるコールバックをイベントで少し解決する に頂いたコメントの内容を理解するべく、読んだり書いたりしてみた。
超長いので先に結論
食わず嫌いだった。
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'));