以下のように「複数のコールバック呼び出しが完了した時点で処理をすすめる」パターンを簡素に書くことができます。
各コールバックに渡された引数もまとめて最後の then 関数に渡され、しかもその順番は cb.here を呼び出した順番と対応することが保証されます。(コールバックが呼び出された順ではありません)
これで複数リソースの読み込みも怖くない!
example.js
var cb = new Callback();
request(..., cb.here());
request(..., cb.here());
request(..., cb.here());
cb.then = function(result1, result2, result3) {
}
callback.js
var Callback = (function () {
/**
* 複数のコールバックをまとめて一つのコールバックにします。
* @constructor
* @example
* var cb = new Callback();
* asyncFunc1(..., cb.here()); // f('guitar') とコールバックされる
* asyncFunc2(..., cb.here()); // f('fiddle','banjo') とコールバックされる
* asyncFunc3(..., cb.here()); // f() とコールバックされる
* asyncFunc4(..., cb.here([])); // f('mandolin') とコールバックされる
* cb.then = function(a, b, c, d) {
* // cb.here() を呼んだ数だけ引数が渡る
* // a は 'guitar'
* // b は ['fiddle', 'banjo']
* // c は null
* // d は ['mandolin']
* }
*/
var Callback = function() {
this.resultList = [];
this.stepCount = 0;
this.callCount = 0;
/**
* すべてのコールバックをまとめて最終的に呼び出される関数。
*/
this.then = null;
};
/**
* コールバック関数を生成して返す。
* 生成した関数が空引数で呼ばれた場合は null を。
* 1引数で呼ばれた場合はその値を。
* 2引数以上で呼ばれた場合は配列を登録し、
* すべてのコールバックが呼び出された時点で
* 登録した引数を then に渡して呼び出す。
* here に空配列を渡した場合は引数の数にかかわらず配列を登録する。
* @return {function(...)}
*/
Callback.prototype.here = function() {
var registerArray = arguments.length == 1 &&
arguments[0] instanceof Array &&
arguments[0].length == 0;
if (arguments.length != 0 && !registerArray) throw new Error(
"Callback#here に引数が渡されました。" +
"xx.here() と書くべき場所で xx.here としている可能性があります。");
return (function(callback, stepCount) {
return function() {
var result = arguments;
if (!registerArray && result.length == 0) {
callback.resultList[stepCount] = null;
} else if (!registerArray && result.length == 1) {
callback.resultList[stepCount] = result[0];
} else {
callback.resultList[stepCount] = [];
for (var i = 0; i<result.length; i++) {
callback.resultList[stepCount][i] = result[i];
}
}
if (++callback.callCount >= callback.stepCount) {
if (callback.then != null) {
callback.then.apply(null, callback.resultList);
Callback.call(this);
} else throw new Error("then が未登録です");
}
}
})(this, this.stepCount++);
};
return Callback;
})();