Ajaxでの戻り値を処理するコールバック関数を動的に受け渡す

経験則的に、たいていWebアプリを開発していて処理経路が煩雑化してしまう部分がAjax処理部分だ。直近の開発では、その部分をできる限り汎用化しようと思って、コールバック型のAjax処理を作ってみた。
建付けとしては、Ajaxの処理をクロージャ的に定義しておき、Ajaxの処理後に実行するコールバック関数名を引数で渡して、Ajax後の戻り値と合わせてコールバック関数を実行させるというものだ。色々と試行錯誤しながら実装できたので、備忘録として残しておく。

## JavaScriptでのコールバック処理

まず、コールバック処理のメインとなるのが、「JavaScriptで関数名を文字列として動的に指定して、関数コールを行う」という部分だ。これにはいくつか方法があるが、下記の３つが使い勝手的に良い感じがした。

**1. `eval()` を使う**

```js:
// 関数名を変数として定義
var function_name = 'callbackFunction';
// 動的関数名の実行
eval(function_name + "();");

// コールバック関数の実体
function callbackFunction() {
  alert('Hello!');
}
```



**2. Functionコンストラクタを使う。**

```js:
// 関数名を変数として定義
var function_name = 'callbackFunction';
// 受け取った関数名を関数として実行するためのコンストラクタ
var Callback = new Function('return ' + function_name + '();');
// 動的関数名の実行
Callback();

// コールバック関数の実体
function callbackFunction() {
  alert('Hello!');
}
```

**3. コールバック用のクラスを準備する。**

```js:
// コールバック用のクラスを定義
var CallbackClass = function() {
  
  this.callbackMethod = function() {
    alert('Hello!');
  }
  
};
// クラスをインスタンス化
var Callback = new CallbackClass();

// 関数名を変数として定義
var function_name = 'callbackMethod';
// 動的関数名の実行
Callback[function_name]();
```

一番お手軽なのはやはり `eval()` を使う方法だ。
しかし、 `jshint` でJavaScriptの構文制御を行っていたりすると、デフォルトで設定値 `evil` が false になっているため、1. と 2. はエラーになってプリコンパイルが通らない。
`.jshintrc` に `"evil": true` を設定して `eval()` と `new Function` を許可してもいいのだが、JavaScriptのセキュリティポリシー的に安全性が失われてしまうのでそれはしたくなかった･･･。
──ということで、私の場合は 3. の一択しかなかった。まぁ、一番スマートな方法かもしれない。


## Ajax処理のクロージャ

Ajax処理は結構カオスになりやすい部分なので、どんなAjaxでも汎用的に処理できるように、できる限りシンプルにして、再利用し易いように無名関数として定義しておく。
なお、Ajax処理はおなじみの jQuery である。

```js:
// jQueryのトップレベルスコープにAjaxレスポンスを格納するオブジェクトをあらかじめ準備しておく
$.ajaxResponse = {};

// 汎用Ajax処理
var CallAjax = function(){
  if (arguments.length < 2) {
    return false;
  }
  var ajax_url = arguments[0];
  var method = arguments[1];
  var post_data = typeof arguments[2] !== 'undefined' ? arguments[2] : null;
  var data_type = typeof arguments[3] !== 'undefined' ? arguments[3] : 'text';
  var callback_function = typeof arguments[4] !== 'undefined' ? arguments[4] : null;
  
  var jqXHR = $.ajax({
    async: true,
    url: ajax_url,
    type: method,
    data: post_data,
    dataType: data_type,
    cache: false,
    beforeSend: function(xhr, set) {
      // return;
    }
  });
  
  jqXHR.done(function(data, stat, xhr) {
    console.log({
      done: stat,
      data: data,
      xhr: xhr
    });
    if ('script' !== data_type) {
      $.ajaxResponse = { 'responseText': jqXHR.responseText, 'status': jqXHR.status, 'statusText': jqXHR.statusText };
    } else {
      return data;
    }
    if ('' !== callback_function) {
      return Callback[callback_function]();
    }
  });
  
  jqXHR.fail(function(xhr, stat, err) {
    console.log({
      fail: stat,
      error: err,
      xhr: xhr
    });
  });
  
  jqXHR.always(function(res1, stat, res2) {
    console.log({
      always: stat,
      res1: res1,
      res2: res2
    });
    if (stat === 'success') {
      alert('Ajax Finished!');
    }
  });
  
};
```

──で、Ajaxしたくなったら、

```js:
CallAjax( './ajax.php', 'post', { 'event': 'retrieve_html' }, 'html', 'render_html' );
```

──のように呼び出す次第。
例えば、上の呼び出し例では、Ajaxの処理実体である `ajax.php` をPOSTコールして、HTML形式の文字列を取得し、コールバック関数として `render_html` を呼び出すような建付けである。
Ajaxの通信後の受け処理としては、Ajaxの戻り値がJavaScriptのコマンドだった場合は、そのまま実行し、それ以外の場合は、 `$.ajaxResponse` オブジェクトに格納している。もし、コールバック関数名が指定されていた場合は、その後にコールバック関数を呼び出すという流れだ。

## コールバッククラスによるAjax後のコールバック処理

コールバッククラスの定義例としては、下記のようになる。

```js:
// Callback処理用のクラス
var CallbackClass = function() {
  
  this.render_html = function(){
    
    $('body').append( $.ajaxResponse.responseText );
    
  };
  
  
};
var Callback = new CallbackClass();
```

クラス内の各メソッドがAjaxのコールバック関数になるという次第だ。例えば、 `render_html` メソッドではAjaxで受け取った HTML 文字列を `<body>` タグ内の末尾に追加するというjQuery処理を行っている。

あとはAjaxの戻り値に対して何か処理をしたい時に、このクラスに専用のメソッドを追加していくだけで、色んなケースのAjaxに対応できるようになる。