深くなるコールバックをイベントで少し解決する

#domのreadyで、ajaxでトークンが有効か確認して、有効ならajaxで一覧データが取得したい

　あると思います。
　※長いので、本題から読んで頂いて問題ないです。

　何も考えずに書く、こんな処理になるかと。

```js
$(document).ready(function(){
            $.ajax(
                {url:'./renew_token.php',
                 data:{
                     token:window.localStorage?localStorage.getItem('token'):"" 
                 }
                }
            ).then(function(auth_response){
                :
                $.ajax({
                    :
                }).then(function(some_response){
                    :
                })
            }).catch(function(err){
                :
            });
        });
```
　これ、非常に嫌いです。
　読むのもつらければ、修正するのもつらい。
　一般的には、これは関数に切り出すべきだといわれます。

```js
    function auth(auth,callback){
        :
        callback(xxx);
        return ;
    }
    function get_list(list_conditions,callback){
        :
        callback(list_data);
    }
```

　一見、すっきりしたように見えて好ましいですが、果たして本当でしょうか？
　こんな感じで使う？

```
	auth({xxxxxx},getlist);
```

　まぁ今の状態ではだいぶすっきりしてますが、もし、認証に失敗したときになんらかの処理をしたい、と仕様変更があったらくそめんどくさいです。
　最初から考えとけよ、という話にもなりますが、そもそも、リストを取得するのと、認証するのは別問題であって、認証に成功しなくても特定のリストは表示したいとか、もうあとから嫌な事を言われるのが目に見えてます。
#本題

　そこで、こういうのを使ってます。

```js:emitter.js
emitter = function create_emittor(){
    var e = {};
    e.jq = $({});
    e.on = function(event,callback){
        e.jq.on(event,function(x,arg){
            callback(arg);
        });
    }
    e.trigger = function(event,args){
        e.jq.trigger(event,args);
    }

    e.proxy = function(event_success,event_error){
        if(!event_error) event_error = event_success;
        return function(err,result){
            if(!err) setTimeout(function(){e.trigger(event_success,result)});
            else setTimeout(function(){e.trigger(event_error,err)});
        }
    };
    e.proxyone = function(event){
        return function(result){
            setTimeout(function(){e.trigger(event,result)});
        }
    }
    return e;
}
var $e = emitter();
```

※余談にも書きましたが、これを書いたときに手元にjQueryがあったから利用しているだけで、on,triggerがあれば何でもいいです。

##使い方

```js
var $e = emitter();
$(document).ready($e.proxyone('renew_token'));

$e.on('renew_token',function(){
    $.ajax({
        url:'./renew_token.php',
        data:{
            token:window.localStorage?localStorage.getItem('token'):"" 
        },
        dataType:"json"
    })
    .then($e.proxyone('token_refreshed'))
    .catch($e.proxyone('token_rejected'));
});

//リフレッシュされたら、リストを取得
$e.on('token_refreshed',$e.proxyone('get_list'));
$e.on('token_rejected' ,$e.proxyone('redirect_to_error'));

$e.on('get_list',function(token){
    //トークンで、リストを取得
    alert(token);
    var list = {};
    //なんかリスト取得処理
    list.list = ['a','b','c'];
    $e.trigger('refresh_list',list);
});

$e.on('refresh_list',function(list){
    //もらったデータを処理
    for(var idx in list.list){
        alert(""+idx+":"+list.list[idx]);
    }
})
```

また、indexedDBのような非同期なDBを使用するときも、以下のような雰囲気でできます。

```js:
//サンプル関数：一度目は失敗し、二度目は成功する。
var count = 0;
function some_err_result_function(callback){
    if(count++==0) {
        callback('error');
        return;
    }
    callback(null,'ready');
}

$(document).ready($e.proxyone('init_xxxdb'));
var _db = null;
$e.on('init_xxxdb',function(){
    //DB初期化
    some_err_result_function($e.proxy('xxxdb_ready','xxxdb_error'));
});

$e.on('xxxdb_ready',function(db){
    //準備OK
    _db = db;
    alert('db_ready');
})
$e.on('xxxdb_error',function(){
    alert('db_error');
    setTimeout($e.proxyone('init_xxxdb'),500);//500ms後にもう一回チャレンジ
})
```

　と、割と柔軟に再試行、再描画が行えます。
　また、イベントのため、トークンがリフレッシュされたら、localStorageにも保存したい、なんて~~当たり前だけど忘れてた~~仕様の追加が起こっても、既存のロジックと完全に切り離して追加実装できます。

```js
$e.on('token_refreshed',function(token){
    if(window.localStorage) window.localStorage.setItem('token',token);
});
```

　単純にリスナを追加するだけですね。

　また、サンプルにあるように、未実装のイベント(redirect_to_error)があっても特に死ぬことはないので、あとから後始末追加したりも割と簡単です。

##ちょっと良いおまけ
　webworker化したり、serviceworker化するのが楽です。

##余談

　on,triggerだけを行うために、jQueryを使いたくない場合は、適当に実装したバージョンもあります。

```js
    e._handlers = {};
    e.on = function(event,eventHandler,eventHandlerName){
        if(!event)        throw 'EventName Required';
        if(!eventHandler) throw 'eventHandler Required';
        if(!e._handlers[event]) e._handlers[event] = [];
        if(eventHandlerName) eventHandler._eventHandlerName = eventHandlerName;
        e._handlers[event].push(eventHandler);
    };
    e.off = function(event,eventHandler,eventHandlerName){
        if(e._handlers[event]){
            for(var k in e._handlers[event]){
                if(!e._handlers[event].hasOwnProperty(k)) continue;
                if(eventHandler && e._handlers[event][k]==eventHandler){
                    delete e._handlers[event][k];
                }else if(eventHandlerName && e._handlers[event][k]["_eventHandlerName"]==eventHandlerName){
                    delete e._handlers[event][k];
                }
            }
        }
    };
    e.once = function(event,eventHandler){
        var handlerName = date().now + Math.random();
        var _this = this;
        e.on(event,function(){
            e.off(event,null,handlerName);
            return eventHandler.apply(t,arguments);
        },handlerName);
    };
    e.trigger = function(event,...args){
        if(!e._handlers[event]) return;
        for(var k in e._handlers[event]) if(e._handlers[event].hasOwnProperty(k)) setTimeout(function(){
            e._handlers[event][k].apply(e._handlers[event][k],args);
        },0);
    };
    //proxy以降は同一です。
```

　こっちのコードで動いている実績はビミョーですが、動かしてみたところなんとなくは動いているようです。
　バブリングさせたかったら、jQueryバージョンのほうがお手軽ですね。

#追記
　敏感に反応してしまってすみませんが、Promise案件というTweet拝見しました。
　ちょっと言葉足らずだったかな、と~~言い訳~~補足しておきます。
　Promiseで解決した結果を、結果に意味を持たせて疎に次の処理へ続けたい、という意図があります。
（わざわざjQueryのthenとcatchを分解しているところで伝わると思っていましたが、言葉足らずでしたね。）