関数の数珠つなぎ

nueでは、独立に定義された関数を数珠つなぎにしています。
その中核を担っているコードはこんなです。

function chain(functions) {
  return functions.reduceRight(function (next, curr) {
    return function () {
      curr.apply({next: next}, arguments);
    }
  });
}

このコード、短い割には使い勝手がいいんじゃないかと思っています。
ちょっと使ってみましょう。a, b, cという3つの関数を定義して数珠つなぎしたあと実行してみます。

var slice = Array.prototype.slice;

function a() {
  console.log('a is called. args: ' + slice.call(arguments));
  this.next('aaa', 123);
}

function b() {
  console.log('b is called. args: ' + slice.call(arguments));
  this.next('bbb', 345);
}

function c() {
  console.log('c is called. args: ' + slice.call(arguments));
}

var f = chain([a, b, c]); // ここで最初に示したchain関数を呼ぶ
f('hoge');


実行結果はこうなります。

a is called. args: hoge
b is called. args: aaa,123
c is called. args: bbb,345


この例では、this.nextしたら次の関数を呼ぶようにしていますが、returnしたら戻り値を引数にして次の関数を呼ぶようにしたり、間になんか処理を挟んだりするのも容易です。


じつは、この数珠つなぎのコード、ASP.NET MVCの次のコードからヒントを得たものだったりします。
http://aspnet.codeplex.com/SourceControl/changeset/view/72551#266452

Func<ActionExecutedContext> thunk = filters.Reverse().Aggregate(continuation,
  (next, filter) => () => InvokeActionMethodFilter(filter, preContext, next));


関数とクロージャの組み合わせはほんとに便利ですね。