node.js Q 使い方 メモ

node.jsでpromiseを使う機会があったので、自分のメモ用に残します。

## Promiseって何？
Promiseって何よ？ってことで、まずはググる。

```
 Promiseとは非同期処理を上手く扱う為のAPIであり、パターンである。 
 非同期の処理の完了後に続けて処理を行いたいとき、よくコールバックパターンが使われるが、処理が連続するとコールバック地獄と言われる分かりづらいソースコードになってしまう。
 また、複数の非同期処理が完了した時に処理を行うなど、コールバックパターンでは難しい事をスマートにできるのがこのPromiseである。

```
要するに、同期処理したいけど、コールバック地獄で見づらくなるのを防ぐために使うAPIってことかな？

npmのパッケージとしては使用可能なモジュールはいくつかあるが、今回は[Q](https://github.com/kriskowal/q/wiki/API-Reference "Q")を使用する。

## Q の基本 (.when .then .done)
簡単なQの記述は下記のようになる

```js
var Q = require('q');

//非同期処理
var stepA = function(val) {
    var d = Q.defer();
    setTimeout(function(){
        console.log(val);
        d.resolve('fuga');
    }, 500);
    return d.promise
};

//同期処理
var stepB = function(val) {
    var d = Q.defer();
    console.log(val);
    d.resolve('piyo');
    return d.promise    
};

Q.when('hoge')
 .then(stepA)
 .then(stepB)
 .done(function(val){
    console.log(val);
});

```

下記はresult

```
hoge
fuga
piyo
```

Q.whenで最初の引数をset, .then(stepA)で、whenでsetした引数でstepAを呼び出し、d.resolveで次のメソッドへの引数をsetする。

.doneは最後に呼ばれる。

このようにPromiseを用いることで、同期・非同期に関係なく簡単に逐次処理を書くことが出来る。

## forEachの処理 (.all)
下記の様な、forEachに対して、Promiseを用いる。

```js
var func = function(val){
    //非同期処理
    setTimeout(function(){
        var result = val*val;
        d.resolve(result);
    }, 500);
    return result;
};

var arr = [1, 2, 3, 4, 5];

arr.forEach(function(val, index){
    result = func(val);
    console.log(result);
});
```
この場合、下記のように書く

```js
var func = function(val){
    var d = Q.defer();
    //非同期処理
    setTimeout(function(){
        var result = val*val;
        d.resolve(result);
    }, 500);
    return d.promise;
};

var arr = [1, 2, 3, 4, 5];

Q.all(arr.map(func));
 .then(function(data){
    console.log(data);
 });

```

```
[ 1, 4, 9, 16, 25 ]

```

.allは、複数のメソッドを同時に実行して、全て完了したら、次の処理を行う。

.allと合わせてforEachのmapやreduceメソッドを使うことで、arrayの全要素に処理を行ってから、次の処理に移れる。

###.spread
.spreadは.allの処理全てが終わった後に実行される。この時、spreadで実行するメソッドの引数には、.allでresolveした値が入る。

.spreadを用いる場合、上記処理は下記のようになる。

```js
var arr = [1, 2, 3, 4, 5];

Q.all(arr.map(func));
 .spread(function(res1, res2, res3, res4, res5){
    data = [res1, res2, res3, res4, res5];
    console.log(data);
 });
```

## try-catch (.fail)
Qでは、.failを用いることで、ステップを途中で抜けることが出来る。

```js
var stepA = function(val) {
    var d = Q.defer();
    setTimeout(function(){
        console.log("StepA:"+val);
        d.resolve('fuga');
    }, 500);
    return d.promise
};

var stepB = function(val) {
    var d = Q.defer();
    setTimeout(function(){
        console.log("StepB:"+val);
        d.reject('piyo');
    }, 500);
    return d.promise
};

var stepC = function(val) {
    var d = Q.defer();
    setTimeout(function(){
        console.log("StepC:"+val);
        d.resolve('hoge');
    }, 500);
    return d.promise
};

function QTest(){
  Q.when('hoge')
    .then(stepA)
    .then(stepB)
    .then(stepC)
    .fail(function(val){
      console.log("fail:"+val);
      var d = Q.defer();
      d.resolve('fail');
      return d.promise
    })
    .done(function(val){
      console.log("done:"+val);
    });
}

```

```
StepA:hoge
StepB:fuga
fail:piyo
done:fail
```
このように、ステップ内でrejectを検知したら、.failを実行してステップが終了する。

なお、failでステップを途中で抜けても、.doneは最後に必ず実行される。