Node.jsを使ったプログラムを書いていて、同期的に処理してほしいところを全て非同期でやっちゃう可愛いnodeのおかげでいくつか勉強になったことをまとめる。
同期処理を考える手順
1.まずはコールバック関数を使う
まず最初に思いつくのはこれ。コールバックを使えば、処理1の値を使った処理2みたいなことができる。
が、簡単な処理に関してはこれで十分だが、処理が複雑になってくるといわゆる「コールバック地獄」と言われる見た目も処理も地獄のようなプログラムができてしまう。要はネストが深くなって何がなんだかよくわからなくなる。
function処理2{callback(処理1)}
2. Promiseを使う
そこで登場するのがPromise。こいつは結構優れもので、書き方を覚えてしまえばコールバック地獄を回避できる。
そもそもPromiseは何をしているかを簡単に説明すると、オブジェクト(引換券的なもの)を先に渡しておいて、resoleが返ってきた、つまり処理が終わればそのオブジェクトと結果を交換するよって感じ。
もっと砕いて言うと、電子レンジがPromiseで、人は何かを温めるとき電子レンジに食べ物を入れる。これが自分のものだよって言うことを示す引換券をもらうとして、温めが終わったら取りにいくイメージ。その間は何をしててもokだから要はPromise単体だと非同期処理を行なっている。
varresult_1=newPromise(function(resolve){resolve("result_1成功");});result_1.then(function(){//処理}).then(fucntion(){})・・・
同期処理にしたければPromiseのthenを使う。これは次々に処理を記述しているが、結局処理が終わらない(resoleが返ってこない)と次に進まないので同期的な処理になる。thenを使えば、コールバックを綺麗に記述でき、メンテナンス性も向上する。
async/awaitを使った方がもっと綺麗
これに関してはいろんなサイトでごちゃごちゃ書いているが、要は使えたらいいので簡潔に言うと、asyncな関数で待ちたい処理をawaitにするだけ。
// await式を使いたいので、async関数を宣言するconstmain=async()=>{// 処理1のPromiseを宣言するconstroutine1=newPromise(resolve=>{//略resolve('end')})constresult1=awaitroutine1;// 処理1の完了を待ち結果を受け取る// 処理2以下を記述……}}// 関数を実行main();
async/awaitが最も綺麗にかけるのでおすすめ。
注意事項 Promiseを変数に入れるか関数のreturnにするか
constroutine1=newPromise(resolve=>{//略resolve('end')})functionroutine1(){returnnewPromise(resolve=>{//略});}
この違いは結構大事。変数のroutine1を呼び出す場合は、呼び出す前にこの代入式を記述するとその時点で処理が実行される。一方で関数の場合は、呼び出した時点で初めてPromiseの処理が始まるという違い。
また、ループでなんどもPromise処理を使いたい時には、functionで毎回呼び出さす必要がある。変数だと一回resolveを返してしまえばそのPromiseは御役御免となり一生使えない。