経験則的に、たいてい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に対応できるようになる。