【node.js】asyncで同期処理【サンプルリポジトリ公開】

node.js では非同期の動きに苦労することが多いのですが、同期処理をサポートするモジュール「async」の動きを実際に動作できる形で紹介します。

#asyncについて
assyncについては以下を参照のこと。
##リポジトリ
https://github.com/caolan/async

##公式ドキュメント
http://caolan.github.io/async/

##Node.jsへの導入
```
npm install async
```

#使用例
##series
順次処理

###記述例
```js
    console.log("-- Series test start ----------");
    async.series([
        function (callback) {
            console.log("1");
            setTimeout(function() {
                console.log("1 done");
                callback(null, 1);
            }, 100);
        },
        function (callback) {
            console.log("2");
            setTimeout(function() {
                console.log("2 done");
                callback("exit!", 1);
            }, 100);
        },
        function (callback) {
            console.log("3");
            setTimeout(function() {
                console.log("3 done");
                callback(null, 1);
            }, 100);
        },
    ], function (err, results) {
        if (err) {
            console.log("err[" + err + "]");
        }
        console.log("- Series test done [" + results + "] ----------");
    });
    console.log("Series test end of line");
```

###結果
```
-- Series test start ----------
1
Series test end of line
1 done
2
2 done
err[exit!]
```

##waterfall
順次処理（次の関数に値を渡せる）
###記述例
```js
    console.log("-- Waterfall test start ----------");
    async.waterfall([
        function (callback) {
            console.log("1");
            setTimeout(function() {
                console.log("1 done");
                callback(null, 1);
            }, 100);
        },
        function (arg, callback) {
            console.log("from:" + arg);
            console.log("2");
            setTimeout(function() {
                console.log("2 done");
                callback("exit!", 2);
            }, 100);
        },
        function (arg, callback) {
            console.log("3");
            setTimeout(function() {
                console.log("3 done");
                callback(null, 1);
            }, 100);
        }
    ], function (err, results) {
        if (err) {
            console.log("err[" + err + "]");
        }
        console.log("- Waterfall test done [" + results + "] ----------");
    });
    console.log("Waterfall test end of line");
```

###結果
```
-- Waterfall test start ----------
1
Waterfall test end of line
1 done
from:1
2
2 done
err[exit!]
- Waterfall test done [2] ----------
```

##eachSeries
コレクションの要素に基づいた同期処理
###記述例
```js
    console.log("-- EachSeries test start ----------");
    var a = [1,2,3,4,5,6,7,8,9,10];
    async.eachSeries(a, function(i, callback){
        setTimeout(function() {
            console.log('eachSeries : ' + i);
            if (i > 5) {
                callback("exit!");
            } else {
                callback(null);
            }
        }, 100);

    }, function(err){
        if (err) {
            console.log("err[" + err + "]");
        }
        console.log("- EachSeries test done" + " ----------");
    });
    console.log("EachSeries test end of line");
```

###結果
```
-- EachSeries test start ----------
EachSeries test end of line
eachSeries : 1
eachSeries : 2
eachSeries : 3
eachSeries : 4
eachSeries : 5
eachSeries : 6
err[exit!]
- EachSeries test done ----------
```

##mapSeries
コレクションの要素に基づいた同期処理（マップ使用）
###記述例
```js
    console.log("-- MapSeries test start ----------");
    var m = [
        { id: 1, value:'first' },
        { id: 2, value:'second' },
        { id: 3, value:'third' },
    ];
    async.eachSeries(m, function(item, callback){
        setTimeout(function() {
            console.log('mapSeries : id[' + item.id + '] value[' + item.value + ']');
            if (item.value == 'second') {
                callback("exit!");
            } else {
                callback(null);
            }
        }, 100);

    }, function(err){
        if (err) {
            console.log("err[" + err + "]");
        }
        console.log("- MapSeries test done" + " ----------");
        testComplete()
    });
    console.log("MapSeries test end of line");
```

###結果
```
-- MapSeries test start ----------
MapSeries test end of line
mapSeries : id[1] value[first]
mapSeries : id[2] value[second]
err[exit!]
- MapSeries test done ----------
```

##timesSeries
回数を指定しての同期処理
###記述例
```js
    console.log("-- TimesSeries test start ----------");
    async.timesSeries(10, function(i, callback){
        setTimeout(function() {
            console.log('timesSeries : ' + i);
            if (i > 5) {
                callback("exit!");
            } else {
                callback(null);
            }
        }, 100);

    }, function(err){
        if (err) {
            console.log("err[" + err + "]");
        }
        console.log("- TimesSeries test done" + " ----------");
        testComplete()
    });
    console.log("TimesSeries test end of line");
```

###結果
```
-- TimesSeries test start ----------
TimesSeries test end of line
timesSeries : 0
timesSeries : 1
timesSeries : 2
timesSeries : 3
timesSeries : 4
timesSeries : 5
timesSeries : 6
err[exit!]
- TimesSeries test done ----------
```

##組み合わせ
上記の複数の同期処理を呼ぶ出す同期処理
###記述例
```js
async.series([
    function (callback) {
        series.test(function() {
            callback(null, "series");
        });
    },
    function (callback) {
        waterfall.test(function() {
            callback(null, "waterfall");
        });
    },
    function (callback) {
        eachSeries.test(function() {
            callback(null, "eachSeries");
        });
    },
    function (callback) {
        mapSeries.test(function() {
            callback(null, "mapSeries");
        });
    },
    function (callback) {
        timesSeries.test(function() {
            callback(null, "timesSeries");
        });
    },
    function (callback) {
        each.test(function() {
            callback(null, "each");
        });
    },
], function (err, results) {
    if (err) {
        console.log("err[" + err + "]");
    }
    console.log('async test all done. ' + results);
});
```

#サンプルコード
上記の処理を確認できるソースコードを下記に用意したので参考にしてください。
https://github.com/k-neo/tryAsync

---
### 筆者参考
札幌圏でリモート開発、在宅勤務を中心としたシステム開発の会社を経営しています。

[ローラハウス](http://laurahouse.net)