くそが async 使え

Javascript で非同期処理を連結するときは、必ず async か、それに準ずるライブラリや関数を使え。

async とは、 callback を引数に取る関数を、順番に呼んだり、並列に呼ぶ機能をサポートする npm モジュールである。
async を使うことで、非同期処理を直感的に扱うことが出来る。


次のコードは、 async を使って、 taskA, taskB, taskC を順番に呼び出すサンプルである。

// with async
var async = require("async");

var taskA = function(callback){
  console.log("taskA");
  callback();
};
var taskB = function(callback){
  console.log("taskB");
  callback();
};
var taskC = function(callback){
  console.log("taskC");
  callback();
};

async.series([
    taskA,
    taskB,
    taskC
]);

async.series は、配列で渡した関数を、上から順番に処理する関数だ。

ちなみに書くまでもないが、 async を使わないとこんな感じである。

// without async
var taskA = function(){
  console.log("taskA");
};
var taskB = function(){
  console.log("taskB");
};
var taskC = function(){
  console.log("taskC");
};

taskA();
taskB();
taskC();

なんだ煩雑になってるじゃないか(呆れ)
いや、結論を出すのはまだ早い。


ここで、 taskA と taskB の間に、 5秒待つだけの taskD という処理を挟みたくなったとする。
async ならこうだ。

// with async
var taskD = function(callback){
  console.log("taskD");
  setTimeout(callback, 5000);
}

async.series([
    taskA,
    taskD, // <- ここに追加
    taskB,
    taskC
]);

そのまま、 5秒後に callback を呼ぶ taskD を書き、配列に挟むだけである。

async を使わない時は、どうするだろうか。
書き方は色々あると思うが、愚直に書くなら、こうだろう。

// without async
var taskD = function(callback){
  console.log("taskD");
  setTimeout(callback, 5000);
}

taskA();
taskD(function(){
  taskB();
  taskC();
});

taskD の後の処理を、全て callback の中に包む必要がある。
匿名関数がひとつ増えるし、 taskA ~ taskD は同列なものであるはずなのに、コードから読み取ることが出来ない。


さらに「新たな非同期処理 taskE を追加して、 taskD と taskE が両方完了したら、 taskB, taskC を呼ぶ」ような処理になったら、どうするだろうか。
ひょっとして、フラグを使う?

// without async
var doneTaskD = false;
var doneTaskE = false;

var taskAfterTaskDOrTaskE = function(){
  if(!doneTaskD || !doneTaskE) return;
  taskB();
  taskC();
};

taskA();
taskD(function(){doneTaskD = true; taskAfterTaskDOrTaskE();});
taskE(function(){doneTaskE = true; taskAfterTaskDOrTaskE();});

UGHHHHHHHHH
たった2つの処理をするためだけに、いくつもの変数や関数を作らなくてはいけないし、
非同期でいろんな処理を行き来するから、コードを上から下へ読むことが出来ない。

しかし、こういった処理は、しばしば要求される。
例えば、「画像のプリロードを全て待ってから、次の処理へ移行する」ような処理は、
ちょっと高級なペライチならよくあるはずだ。

async で同じ処理を書くとこうなる。

// with async
async.series([
    taskA,
    function(callback){
      async.parallel([
        taskD,
        taskE
      ], callback);
    },
    taskB,
    taskC
]);

どうだろうか。
async.parallel は、配列で渡した関数を、並行に実行し、
全ての処理が終わった時に、唯一度だけコールバック呼び出す関数だ。

くそったれなフラグも使わないし、関数だって匿名関数がひとつ増えただけである(それだって、 bind で書けば必要なくなる)。
そして何より、行う処理が、上から順番通りに並んでいることが重要だ。


ブラウザ上で async を使いたいときは、 適当な git リポジトリからクローンしてくることも可能だが、
browserify を使えば、 node と全く同じように require して使うことが出来る。

ちなみにメソッドチェーンは関数を用意するのが面倒な上、
なにがしQuery は、どうせ .queue とか使っちゃうので論外である。

願わくば世界から一つでも多くのコールバック地獄が減ることを。