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

  • 24
    Like
  • 0
    Comment
More than 1 year has passed since last update.

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

JavaScriptでのコールバック処理

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

1. eval() を使う

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

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

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

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

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

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

// コールバック用のクラスを定義
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 である。

// 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したくなったら、

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

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

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

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

// 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に対応できるようになる。