今回初めて社外勉強会に出てみたので、その時学んだことをシェアしたいと思います。内容としてはJavascriptの非同期処理を克服しようといったもので、様々な非同期処理の書き方を学んでいきます。
そもそも非同期処理って何?
Javascriptではシングルスレッドといって、処理の実行は前に行われている処理が終了するまで次の処理を実行することができません。これを同期処理とも言いますが、非同期処理では前の処理が終了するのを待たずに次の処理を行うことができます。何故このようなケースが必要なのかというと、例えばAPIを使用して他のサーバーからデータを取得する際に、同期処理ではそのユーザーがデータを取得するまで画面で操作を行えないといったケースが発生しますが、非同期処理を行えばデータ取得を待たずに画面上で操作を続けることができるようになります。
非同期処理の例 その1
ここでは基本的な非同期処理を記載してみます。コードを読んでみて、consoleがどの順番で呼ばれるか想像してみてください。
console.debug('1');
fs.readFile("test.txt", (err, file) => {
if (err) throw error;
console.debug('2 ファイルの読み込み完了')
});
console.debug('3');
ここではNode.jsの提供するファイルの読み書きを行うreadFile関数を使用して、"test.txt"というファイルを非同期で読み込みにいっています。非同期形式の関数は常にコールバック関数を受け取りますので、処理の終了後にコールバック関数が呼ばれる形になります。ここで確認したいのはconsoleが呼ばれる順番ですが、普通に読むと処理は上から実行されていきますので、1,2,3の順番で呼ばそうですが、この処理では1,3,2の順番で呼ばれることになります。これは上記でも説明した非同期処理の特性で、前の処理が終了するのを待たずに次の処理を実行できる為、2のファイル読み込みの処理を行なっている間に3のconsoleが実行されてこの順番になるという訳ですね。
非同期処理の例 その2
次にPromiseを使用した非同期処理を書いてみます。Promiseというのは非同期処理の際に処理が終了したら、その結果を成功または失敗として返すものです。大抵の非同期処理ではPromiseオブジェクトを返してくるので、そのオブジェクトから成功した時の処理はthenメソッド・失敗した時はcatchメソッドを使用することで、その後の処理を記載することができます。
fs.readFile('test1.txt')
.then(() => {
return readFile('text2.txt');
})
.then(() => {
return readFile('text3.txt');
})
.then(() => {
return readFile('text4.txt');
})
.catch((error) => {
console.error(error);
})
上記ではtest1.txtを最初に呼び出し、その処理が終わったらtest2.txtを呼び、その次はtest3.txtを呼ぶといった感じで、非同期処理で順番に実行したい内容を指定して処理を行なっています。ここでも実行後の処理はコールバック関数で登録しています。
非同期処理の例 その3
最後にasync/awaitを用いた非同期処理を書いていきます。
async function run() {
await readFile('test1.txt')
await readFile('test2.txt')
await readFile('test3.txt')
await readFile('test4.txt')
}
run();
ここではrun関数を定義し、非同期処理の後ろで呼ぶようにしています。実際の処理の動きとしては、その2と同じになります。asyncとは関数を非同期関数として扱うことができ、返り値が暗黙的にPromiseオブジェクトになります。そしてawaitというのは基本的にasyncの中でのみでしか使用できないのですが、こちらを使用することでthenを使用した時と同じようにawait以降の処理をコールバック関数として受け取り処理を行うことができます。こうして見ると、その2で書いた書き方よりも非常にスッキリして書けているように見えますね。連続して非同期処理を書きたい場合はthenよりもこちらの書き方の方が良さそうです。
終わりに
今回は勉強会で学んだ内容を簡単にまとめてみました。私自身これまで非同期処理を連続して書くケースは少なく、thenで事足りるケースが多かったですが、今後そういったケースに出くわした際にはぜひasync/awaitを用いた書き方にチャレンジしてみたいと思います。