Javascript で複数のコールバックをまとめて受け取る

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

以下のように「複数のコールバック呼び出しが完了した時点で処理をすすめる」パターンを簡素に書くことができます。
各コールバックに渡された引数もまとめて最後の 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;
})();