Help us understand the problem. What is going on with this article?

JavaScriptのasync.jsでwaterfallとseries、parallelの違い

More than 3 years have passed since last update.

async.js

主にnodeで使用されている、javascriptの非同期処理を扱うライブラリです。
フロントエンドでもゲーム作り等では重宝するんです。

その中でも使用率がかなり高い、waterfallとseries、そしてparallelの三つについて簡単にまとめました。

さらに、ECMAScript6からは非同期処理を扱うためのPromiseオブジェクトが標準で使用できます。
ECMAScript6のアロー関数とPromiseまとめ - JavaScript

技術情報のみつぶやくアカウント作成しました。JavaScriptなどの最新情報も追っていきます。
Twitter: @takeharumikami

RSSはこちら
Feedly: Feedlyをフォロー
RSS: http://qiita.com/takeharu/feed

waterfall

まず、waterfallについてです。
配列の順番で、この場合は上の関数から実行されていきます。

この際に引数に与えられたcallbackの関数が実行されるまで、
次の関数の処理には移行しません。

そしてcallbackの関数の第一引数にはerrorを、
第二引数以降は次の関数に渡したい引数を選ぶ事ができます。

async.waterfall([
  function(callback) {
    console.log('1');
    setTimeout(function() {
      console.log('1 done');
      callback(null, 1);
    }, 100);
  },
  function(arg1, callback) { // arg1 === 1
    console.log('2');
    setTimeout(function() {
      console.log('2 done');
      callback(null, 1, 2);
    }, 50);
  },
  function(arg1, arg2, callback) { // arg1 === 1, arg2 === 2
    console.log('3');
    setTimeout(function() {
      console.log('3 done');
      callback(null, 1, 2, 3);
    }, 10);
  }
], function(err, arg1, arg2, arg3) { // arg1 === 1, arg2 === 2, arg3 === 3
  if (err) {
    throw err;
  }
  console.log('all done.');
  console.log(arg1, arg2, arg3);
});

//出力結果
1
1 done
2
2 done
3
3 done
all done
1 2 3

series

seriesの流れはwatarfallの制御の流れと同じです。

配列の順番に、この場合は上から順次実行されていきます。
そして、次の関数に処理が移るにはcallbackが呼ばれる必要があります。

違いはcallbackの引数に渡した値ですが、
最後にまとめてこの配列の順番のまま、配列に値が代入されています。

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(null, 2);
    }, 50);
  },
  function(callback) {
    console.log('3');
    setTimeout(function() {
      console.log('3 done');
      callback(null, 3);
    }, 10);
  }
], function(err, results) {
  if (err) {
    throw err;
  }
  console.log('all done');
  console.log(results);
});

//出力結果
1
1 done
2
2 done
3
3 done
all done
[1, 2, 3]

parallel

最後にparallelです。
seriesやwaterfallと違い、並列に処理されます。
(厳密には並列ではないですが)

ですから引数に渡されたcallbackでは次の関数が呼ばれません。

callbackに渡した引数はまとめて最後に配列となって、
この順番通りに代入されています。

async.parallel([
  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(null, 2);
    }, 50);
  },
  function(callback) {
    console.log('3');
    setTimeout(function() {
      console.log('3 done');
      callback(null, 3);
    }, 10);
  }
], function(err, results) {
  if (err) { 
    throw err;
  }
  console.log('all done');
  console.log(results);
});

//出力結果
1
2
3
3 done
2 done
1 done
all done
[1, 2, 3] 

parallelは並列処理なので、
配列に関数を代入しなくてもいいです。

そこで別パターンとしてオブジェクトの各valueに関数を代入する方法があります。

この場合の違いは二点です。

1:並列処理の開始が保証されない
2:callbackに渡した値が、最後にまとめてオブジェクトに代入されている

1は配列の時と違い、どこから実行されるか保証されていないということです。
配列のときは要素の0から実行されていきます。

2は下記を見てもらえればわかるのですが、
同じkeyに対応して渡された値が代入されています。

async.parallel({
  first: function(callback) {
    console.log('1');
    setTimeout(function() {
      console.log('1 done');
      callback(null, 1);
    }, 100);
  },
  second: function(callback) {
    console.log('2');
    setTimeout(function() {
      console.log('2 done');
      callback(null, 2);
    }, 50);
  },
  third: function(callback) {
    console.log('3');
    setTimeout(function() {
      console.log('3 done');
      callback(null, 3);
    }, 10);
  }
}, function(err, results) {
  if (err) { 
    throw err;
  }
  console.log('all done');
  console.log(results);
});

// 出力結果
1
2
3
3 done
2 done
1 done
all done
{ third: 3, second: 2, first: 1 }

まとめ

コールバック処理はjavascriptでは重要な要素ですよね。

asyncを使うとスパゲッティにもなりづらいですし、
うまく扱えるとかなり重宝しますので、ぜひぜひおすすめです。

アカウント

技術情報のみつぶやくアカウント作成しました。JavaScriptなどの最新情報も追っていきます。
Twitter: @takeharumikami

RSSはこちら
Feedly: Feedlyをフォロー
RSS: http://qiita.com/takeharu/feed

おすすめの記事

ECMAScript6からは非同期処理を扱うためのPromiseオブジェクトが標準で使用できます。
ECMAScript6のアロー関数とPromiseまとめ - JavaScript

入門者がつまづく、thisの挙動を4種類に分けて簡単に学ぶならこれ。Apply, callの挙動までわかる。
JavaScriptの「this」は「4種類」?」

JavaScriptでは関数型言語の一部の機能?実践的なJavaScriptの関数型とは。
JavaScriptで関数型プログラミングの入門

takeharu
【名前】三上丈晴 【経歴】北海道大学理学院数学専攻を経て、2012年新卒入社。現在ピグパーティ プロダクトマネージャ 【著書】Git逆引き入門
https://twitter.com/takeharumikami
cyberagent
サイバーエージェントは「21世紀を代表する会社を創る」をビジョンに掲げ、インターネットテレビ局「AbemaTV」の運営や国内トップシェアを誇るインターネット広告事業を展開しています。インターネット産業の変化に合わせ新規事業を生み出しながら事業拡大を続けています。
http://www.cyberagent.co.jp/
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away