Flow.js: 最小限の非同期処理コントローラJavaScript

[uupaaさんのFlow.js](https://gist.github.com/ne-sachirou/5780843)ではなくて、其のFlow.jsの、minimalな機能を再實裝しました。練習以上の意味は有りません(〃l _ l)
See Gist [最小限の非同期処理コントローラJavaScript - c4se記：さっちゃんですよ☆](http://c4se.hatenablog.com/entry/2013/06/14/195740).

API
--
<code>var flow = new Flow(3, callback);</code>と、非同期に待つtaskの數と、callback函數を指定してFlowを生成します。taskは、成功したtimingで<code>flow.pass(value);</code>を呼び出してあげてください。或るtaskが失敗した、或いは指定された個數のtaskが成功した時点で、callbackを呼びます。

```js:
var flow = new Flow(2,
  function(err, passes) {
    if (err)
      throw err;
    else
      console.log(passes);
      // -> ['pass! 1', 'pass! 2'] (順不同)
  });

setTimeout(function() { flow.pass('pass! 1'); },
  Math.floor(Math.random() * 5000));
setTimeout(function() { flow.pass('pass! 2'); },
  Math.floor(Math.random() * 5000));
```

失敗した場合は、<code>flow.miss(err);</code>を呼んでください。

```js:
setTimeout(function() { flow.miss(new Error('miss! 1')); },
  Math.floor(Math.random() * 5000));
```

callbackの引數には

- 或るtaskが失敗した場合: errにはErrorが、passesにはnull
- 全てのtaskが成功した場合: errにはnullが、passesにはpass()に渡された値の配列

が渡されます。Maybeの様なものです。
<code>flow.pass(value, key);</code>にはkeyを渡す事ができます。key附きのpass()とkey無しのpass()は、同時に使えます。

```js:
var flow = new Flow(3,
  function(err, passes) {
    if (err)
      throw err;
    else
      console.log(passes);
      // -> { 'first': 'pass!', 'second': 'pass!' } and passes[0] === 'pass! 3'
  });

setTimeout(function() { flow.pass('pass!', 'first'); },
  Math.floor(Math.random() * 5000));
setTimeout(function() { flow.pass('pass!', 'second'); },
  Math.floor(Math.random() * 5000));
setTimeout(function() { flow.pass('pass! 3'); },
  Math.floor(Math.random() * 5000));
```

失敗可能なFlowも定義できます。<code>var Flow(num, callback, true);</code>と、is_missableにtrueを渡してください。<code>flow.is_missable === true</code>であるFlowは、Errorがthrowされない限り決して失敗しません。

```js:
var flow = new Flow(2,
  function(err, passes) {
    console.log(passes);
  }, true);

setTimeout(function() { flow.pass('pass! 1'); },
  Math.floor(Math.random() * 5000));
setTimeout(function() { flow.miss(new Error('miss! 2')); },
  Math.floor(Math.random() * 5000));
```

現在待ち中のFlowは、<code>Flow.flows</code>にて一覧できます。

Minified
--

```js:Flow.min.js
/*
 Public Domain
*/
(function(e){function b(a,f,c){this.waits=a;this.callback=f;this.is_missable=!!c;this.is_finished=!1;this.passes=[];b.flows.push(this)}function c(a){a.waits-=1;0>=a.waits&&(d(a),a.callback(null,a.passes))}function d(a){a.is_finished=!0;b.flows=b.flows.filter(function(b){return b!==a})}e.Flow=b;b.flows=[];b.prototype={pass:function(a,b){if(!this.is_finished)return b?this.passes[b]=a:this.passes.push(a),c(this),this},miss:function(a){if(!this.is_finished)return this.is_missable?c(this):(d(this),this.callback(a,
null)),this}}})(this);
```

小さいでしょ(〃l _ l)☆

Main
--

```js:Flow.js
/**
 * @description Minimal async flow controller. Like uupaa's Flow.js https://github.com/uupaa/flow.js
 * @author ne_Sachirou <utakata.c4se@gmail.com>
 * @license Public Domain
 */
(function(scope) {
scope.Flow = Flow;

/**
 * @constructor
 * @param {number} waits
 * @param {function(Error,Array.<Object>)} callback callback(err, passes)
 * @param {boolean=} is_missable =false
 */
function Flow(waits, callback, is_missable) {
  this.waits = waits;
  this.callback = callback;
  this.is_missable = !! is_missable;
  /** @type {boolean} */
  this.is_finished = false;
  /** @type {Array.<Object>} */
  this.passes = [];
  Flow.flows.push(this);
}

/** @type {Array.<Flow>} */
Flow.flows = [];

Flow.prototype = {
  /**
   * @param {Object} value
   * @param {string=} key
   * @return {Flow}
   */
  pass: function(value, key) {
    if (this.is_finished)
      return;
    if (key)
      this.passes[key] = value;
    else
      this.passes.push(value);
    check(this);
    return this;
  },

  /**
   * @param {Error} err
   * @return {Flow}
   */
  miss: function(err) {
    if (this.is_finished)
      return;
    if (! this.is_missable) {
      finalize(this);
      this.callback(err, null);
    } else {
      check(this);
    }
    return this;
  }
};

function check(flow) {
  flow.waits -= 1;
  if (flow.waits <= 0) {
    finalize(flow);
    flow.callback(null, flow.passes);
  }
}

function finalize(flow) {
  flow.is_finished = true;
  Flow.flows = Flow.flows.filter(function(elm) { return elm !== flow; });
}

}(this));
```

codeは此ちらに　→ [最小限の非同期処理コントローラJavaScript - c4se記：さっちゃんですよ☆](http://c4se.hatenablog.com/entry/2013/06/14/195740)。