c4se記:さっちゃんですよ☆

.。oO(さっちゃんですよヾ(〃l _ l)ノ゙☆)

.。oO(此のblogは、主に音樂考察Programming に分類されますよ。ヾ(〃l _ l)ノ゙♬♪♡)

音樂は SoundCloud に公開中です。

考察は現在は主に Scrapbox で公表中です。

Programming は GitHub で開發中です。

deferred (延期) を自動で連鎖させる (node.js)

cf. q http://documentup.com/kriskowal/q/

JavaScriptは非同期APIの塊だ。node.jsはもっと然ふだ。非同期を上手く扱へないといけない。方法としては幾つか有る。callback関数をnestさせて頑張る。遅延評価のAPIに変換する。Deferred (Promise, Future) を使ふ。Flow.jsを使ふ。
cf. 最小限の非同期処理コントローラJavaScript http://c4se.hatenablog.com/entry/2013/06/14/195740
node.jsの次versionには、yieldが入るらしい。何年も前にFirefox, Rhinoに実装され、漸く最近ES6に入り、目出度くV8に実装されたものだ。yieldは、C#のasync - awaitの様に使へる。詰まり組み込みのdeferredであり、此れが入れば解決だが、未だ無い。Deferredのlibraryを使おう。
今迄client side (Web browser) で、jQuery.Deferredを使ってきてゐたが、今回node.jsを弄る事に成り、deferredを使ふ事にした。因みにjQuery.Deferredの移植も有るが。
ところが此のdeferred module、documentが詳しいくせに、明解とは言ひ難い。reduceを使って自動で連鎖させやうとしたが、少し困って了った。

前提

test.jsと云ふfileが有り、start(number,function())と云ふ非同期関数がAPIとして公開されてゐるとする。例へば以下の実装だ。

// test.js
'use strict';
/**
 * @param {number} n
 * @param {function()} fn
 */
function start(n, fn) {
  setTimeout(function() {
    console.log(n);
    fn();
  }, 1000);
}

module.exports = {start: start};

此の関数を、1, 2, 3と云ふ引数を与えて、「順番に」実行したいとしやう。普通にやると以下の様に成る。

// index.js
var start = require('./test.js').start;

start(1, function() {
  start(2, function() {
    start(3);
  });
});

人間の頭では追へない。うっかり文法errorとか起こしそうだ。

deferredを普通に使ふ

其所でdeferredを使おう。startをdeferred用の関数でくるんで、doStart(number)としてやる。

// index.js
var deferred = require('deferred'),
    start = require('./test.js').start;

/**
 * @param {number} n
 * @return {deferred.promise}
 */
function doStart(n) {
  var d = deferred();

  start(n, d.resolve);
  return d.promise;
}

// Some code using deferred here.

此のdoStartを、普通に使ふと、此う成る筈だ。

// Some code using deferred.
deferred(start(1))(
  function() { return doStart(2); }
)(
  function() { return doStart(3); }
).done();

functionと打つのが長ったらしいので、Function.prototype.bindを使っても好い。

// Some code using deferred.
deferred(start(1))(
  doStart.bind(null, 2)
)(
  doStart.bind(null, 3)
).done();

JavaScriptはmacroが無い丈のLispだ。みんな此んなにLispを使ってゐる。勝利だ。

此のdeferredを自動で連鎖させる

さて、此所からが本題だが、1, 2, 3と順番に実行する事が判ってゐるのだから、配列を回して自動で書いて了ひたいものだ。ところが上記を見ると解るが、最初の呼び出し丈、形式が違ふ。deferred(1の呼び出し)(2の予約)(3の予約)と成ってゐる。此れが邪魔だ。思い切ってdeferred()(1の予約)(2の予約)(3の予約)としてもerrorと成るし、deferred(1の予約)(2の予約)(3の予約)としてやると、1は実行されない。
実はdeferred()にnullを渡してやれば好い。

// Some code using deferred.
deferred(null)(
  doStart.bind(null, 1)
)(
  dpStart.bind(null, 2)
)(
  doStart.bind(null, 3)
).done();

deferred(null)(1の予約)(2の予約)(3の予約)と云ふ形式に出来る。後は自動で連鎖させるのは簡単だ。

// Some code using deferred.
[1, 2, 3].reduce(function(d, n) {
  return d(doStart.bind(null, n));
}, deferred(null));

以上。