くそが 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 とか使っちゃうので論外である。
願わくば世界から一つでも多くのコールバック地獄が減ることを。