Node.js でバッチ処理プログラムを書くときなどに、ループ処理の中から非同期関数を呼び出すことがある。

素直にコードを書いてしまうと、複数スレッドから非同期関数を同時にコールするような感じになってしまう。その非同期関数がデータベースやネットワークなどのIO系だと、処理がたまってあふれてしまったりもする (データベース接続数が限界に達するとか)。これを解決するために、ループから何度も非同期関数を呼び出すときに、順番に関数を呼び出すよう同期的に処理をしたい。

今回は関数の再帰呼び出しによるループ処理を利用することで、非同期関数を同期させてコールするコードを書いてみた。

以下、4パターンのサンプルコードとその実行結果を置いておく。

  • 普通のループ
  • 再帰呼び出しによるループ
  • 同期型関数をループ内から呼び出す
  • 非同期型関数を再帰呼び出しによるループ内から呼び出す

普通のループ

まず、普通のループ処理を書いてみる。


$ cat loop.js 

// loop by for statement
var i = 0; // counter for loop
var hoge = 0;
for(i=0; i<10; i++){
  hoge += 2;
  console.log('for: ' + hoge);
}

実行結果。


$ node loop.js 
for: 2
for: 4
for: 6
for: 8
for: 10
for: 12
for: 14
for: 16
for: 18
for: 20

再帰呼び出しによるループ

再帰呼出しでループを実現してみる。


$ cat recursive.js 

var i = 0; // counter for loop
var hoge = 0;
outputCounter();

// loop by recursive call
function outputCounter(){
  if(i < 10){
    hoge += 2;
    console.log('recursive: ' + hoge);
    i++;
    outputCounter();
  }
}

実行結果。


$ node recursive.js 
recursive: 2
recursive: 4
recursive: 6
recursive: 8
recursive: 10
recursive: 12
recursive: 14
recursive: 16
recursive: 18
recursive: 20

同期型関数をループ内から呼び出す

同期型関数 sync_hoge をループから呼び出す処理を書いてみる。


$ cat sync_loop.js 

// loop by for statement
var i = 0; // counter for loop
var hoge = 0;

// loop by for statement
for(i=0; i<10; i++){
  console.log('i: ' + i);
  hoge = sync_hoge(hoge);
  console.log('hoge: ' + hoge);
}

function sync_hoge(hoge){
  return hoge + 2;
}

実行結果。


$ node sync_loop.js 
i: 0
hoge: 2
i: 1
hoge: 4
i: 2
hoge: 6
i: 3
hoge: 8
i: 4
hoge: 10
i: 5
hoge: 12
i: 6
hoge: 14
i: 7
hoge: 16
i: 8
hoge: 18
i: 9
hoge: 20

非同期型関数を再帰呼び出しによるループ内から呼び出す

非同期関数 async_hoge を再帰呼び出しによるループから呼び出す処理を書いてみる。

2つの関数が相互に呼び合う形になっている。


$ cat ./async_loop.js 

var i = 0; // counter for loop
var hoge = 0;

myCallback(hoge);

// loop by recursive call
function myCallback(hoge){

  if(i < 10){

    console.log('i: ' + i);

    i++; // countup before calling a function for a fast function

    async_hoge(hoge, function(response){
      console.log('hoge: ' + response);
      myCallback(response);
    });
  }
}

function async_hoge(hoge, callback){
  setTimeout(function(){
    callback(hoge + 2);
  }, 500);
}

実行結果。


$ node ./async_loop.js 
i: 0
hoge: 2
i: 1
hoge: 4
i: 2
hoge: 6
i: 3
hoge: 8
i: 4
hoge: 10
i: 5
hoge: 12
i: 6
hoge: 14
i: 7
hoge: 16
i: 8
hoge: 18
i: 9
hoge: 20

非同期関数を順番に呼び出すだけなら、 Q などの Promise 系ライブラリを使ってコールバック地獄にならないよう、ある程度は綺麗に書ける。非同期関数を呼び出すループ処理も、ライブラリで綺麗に同期処理が書けるなら試してみたい。

tags: node.js javascript

Posted by NI-Lab. (@nilab)