ぼくらはなぜ非同期処理プログラミングをするのか
Node.js Advent Calendar 2015 6日目(遅刻)です。
ぼくはこの 2015 年は React.js や RxJS と出会ってとても JavaScript が楽しくなった一年でした。 移り変わりの激しいこの JS 界隈で様々なモジュールやフレームワークを触ってみて、JavaScript という言語がどういう言語なのかわかってきた気がするので、今回はそれを自分なりに整理してみたものを書いていきたいと思います。
JavaScript の非同期処理
JavaScript では非同期な API が数多くあるため、このように
let data = someAsyncFunc(); console.log(data); //=> undefined
Ruby や PHP のような同期的なスタイルの言語と同じ感覚でコードを書くと、意図した通りに実行されないことがあります。
順番通りに実行するための最も簡単な方法は
someAsyncFunc((data) => { console.log(data); });
このようにコールバック関数を渡して非同期処理の結果を受け取ることです。
しかし非同期処理がいくつもある場合、コールバック地獄と呼ばれる多重にネストするコードを書かなければならなくなるので、それを回避するために
let p = new Promise((resolve) => { someAsyncFunc((data) => { resolve(data); }); }); p.then((data) => { console.log('data: ', data); });
Promise を使ってみたり、
import co from 'co' co(function* () { let data = yield new Promise((resolve) => { someAsyncFunc((data) => { resolve(data); }) }); console.log('data: ', data); })
co を使って Async/Await してみたりするわけです。
ぼくはこれらの「非同期処理を直列に実行する技術」を学んでいたとき、
「順番通りに処理したいだけなのになぜこんな面倒なコードを書かなければいけないんだ」
という疑問を常に抱いていました。
「非同期プログラミングをやる意味はあるのか」
とさえ思ったことがあります。
イベント駆動と非同期プログラミング
JavaScript がイベント駆動型のプログラミング言語であることは多くの人がご存知だと思います。
「イベント駆動ってあれでしょ? addEventListener
とかそいういうやつ。」
var button = document.querySelector('.button'); button.addEventListener('click', function(event) { // クリックした時の処理 });
そうですそれです。
「○○したときに△△する」を記述していくのがイベント駆動プログラミングです。
そしてイベント駆動プログラミングとは、つまるところ非同期プログラミングです。
非同期プログラミングでは、ある関数 A が非同期処理 B を実行した場合、A は B の処理が終わるのを待たずして終了します。 B は処理が完了したら処理結果を A に返すのではなく、直接次の処理 C を呼び出します。
イベント駆動プログラミングではあるイベント A が発火したら、A は次のイベント B を発火し、さらに B は C を発火し... と次々とイベントの発火が伝搬していくように処理が進みます。
これらはどちらも以下の図のように表すことができ、概ね同じようなフローであることがわかります。
処理結果をもとの関数に返すのではなく次々と処理を呼び出していく非同期プログラミングのスタイルは、
このような同期的なスタイルのプログラミングとは全く異なるものであるため、非同期な処理をむりやり同期的なスタイルで記述しようとても辛くなるだけなのです。
だから、「非同期プログラミングつらい」という人に対してぼくが言えるのは
「いっそ全部を非同期スタイルで書いてしまえば逆につらくない」
です。
Promise のような非同期処理のパターンは、リアクティブプログラミングやストリームのようなイベント駆動のもっと大きな枠組みに組み込んではじめてその真価を発揮します。 慣れ親しんだ同期的なスタイルから遠く離れて非同期の大きな流れに乗ることが出来たとき、一気に非同期プログラミングが楽しくなることと思います。
これからの時代に合ったプログラミングパラダイム
ここまでで「非同期プログラミングはつらくない」ということを述べてきましたが、まだ「非同期プログラミングをやるべき理由」にはなっていません。 ぼくが思う非同期プログラミングをやる理由は「これからの時代に求められるアプリケーションは非同期スタイルのほうが向いているから」です。
例えば、昔の Web は同期的なスタイルが向いている分野でした。
HTTP リクエストに対して必要なデータにアクセスしてビューを組み立てて返す、という処理を順番にひとつづつこなしていくことさえやっていれば問題ありませんでした。
しかし今の Web はそうではありません。
サーバーサイドでは通常のページ遷移のリクエストよりも、API に対するリクエストや WebSocket によるイベント処理のほうが多くなってきましたし、 クライアントサイドではユーザーの様々な操作に対してページ遷移なし(非同期)で次々と DOM 構造を書き換えるのが当たり前になってきました。
Web に限らず、あらゆるアプリケーションでリアルタイムなインタラクションが当たり前に求められる時代になってきていて、同期的なプログラミングスタイルだけではそれには追いついていけないのではないでしょうか。
非同期プログラミングは、そういう領域に足を踏み入れるための強力な武器になるのだと思います。