概要
JavaScript(以後「JS」)の非同期処理についてなんだか勘違いしていたっぽいのであえて今のタイミングでまとめておく。
自分の勘違い
ほぼ今までPHP/laravelしか触ってこなかった自分は非同期処理のイメージが湧きにくかった。(言い訳)
非同期処理って「実行結果をきにしない処理のトリガーだけ引いて後は知らなーい」って感じだと誤解してた。全然違った。JSがNode.jsなどのランタイムで動作する時、シングルプロセス(処理するところが一箇所って感じ)で処理が行われる。(PHP/laravelは複数プロセスが並列起動するっぽい。だから非同期処理しなくてもそんなに問題じゃない。)
シングルプロセスの場合、実行中の処理が終わらないと、次の処理が始まらない。遅い処理(DBアクセスによるI/Oなど)があると実行待ちの処理がある場合どんどん遅くなる。そのため、JSでは時間かかる処理はバックグラウンドで「非同期処理」で実行しつつ、次の処理を実行し、バックグラウンド処理が終わったら中断していた処理の続きを再開する。
※厳密に言うとJSの非同期処理は「バックグラウンド処理」ではないらしい。別のプロセスで実行するのではなく、同じプロセス内部でメインスレッドをブロックしないということ。(I/Oが絡む場合は「ノンブロッキングI/O」というらしい)
※Node.jsがシングルプロセスである理由・・・JSの実行がまだブラウザのみでの動作想定だった頃の名残、設計思想を引き継いだため。
得た知見
asyncとawait
ここで非同期処理の命令に必須なasyncとawaitについて簡単にまとめる。
- async:非同期関数の定義時に使う
- await:非同期処理の呼び出し時に使う
下記のように同期関数と非同期関数を定義したとする。
function helloFunction() {
return "hello"
}
async function asyncHelloFunction() {
return "hello"
}
上記をそれぞれ呼び出すと下記の様な戻り値が得られる。
- helloFunction()の戻り値:文字列の「hello」が返却
- asyncHelloFuction()の戻り値:Promiseオブジェクトに「hello」という文字列が入って返却
非同期にしたいが、都度Promiseオブジェクトが返っていたら使いにくい。
そのため非同期処理の呼び出しにはawaitを用いる。下記の様に。
const greet = await asyncHelloFunction();
このようにすることで定数greetには文字列の「hello」が入る。
ただ、awaitは制約があり、asyncで内包された処理でないと使用することはできない。下記はエラーになる。
function helloFunction() {
const greet = await asyncHelloFunction();
}
上記を下記のように修正すればエラーにはならない。
async function helloFunction() {
const greet = await asyncHelloFunction();
}
参考文献