JavaScriptの面倒くさいところに同期処理が面倒くさいというのがある。
ある処理が終了した後はコールバック関数が非同期で呼ばれるというのがよくあり、ちょっと色々かくとコールバック地獄になる
非同期で動く例
axiosというライブラリを使って、このブログにアクセスするコードを書いてみる。
const axios = require('axios'); const main = ()=>{ axios.get("https://glorificatio.org/").then(res=>{ //HTTP ステータスを表示(何もなければ200) console.log(res.status); }); console.log("終了"); } main();
こういうコードの場合、
ウェブページにアクセスする処理をお願いしたら、main関数の処理は先に進み、
先に”終了”という文字を表示させて、
その後にウェブページからのリクエストが帰ってきてHTTPステータスが表示される
なのでこのような表示結果になる
終了
200
async awaitを使って同期をとる
簡単な使い方を説明する
- .then()で非同期処理になる関数を呼び出す時に、awaitをつける
- 戻り値がthenに渡すコールバック関数の引数と同じになる
- awaitが使われる関数にasyncをつける
const axios = require('axios'); const main = async ()=>{ let res = await axios.get("https://glorificatio.org/") console.log(res.status) console.log("終了"); } main();
これでawaitを付けたところ、すなわちaxiosの処理が終わるまでmain関数の処理を待っててくれる
処理結果はさっきとは順番が変わって、ソースコード上の順番になっていて、とてもわかり易くなっている。
処理結果は以下の通り
200
終了
非同期処理を作る
async awaitは非同期処理をする関数がもともとあって、それを同期処理にしたい場合につかう。
逆に、非同期処理を手で作ってみる場合はどうするのか。
さっそくなので作ってみる。
1秒ほどかかってようやく計算をするというメソッドで、
しかもマイナス値だとエラーになるという仕様だ
//非同期な処理をする関数 const osoiProc = (n) =>{ //非同期したい処理はPromiseでくるむ感じ return new Promise((resolve,reject) =>{ let a = 3 * n; //時間が遅くなっているのはサンプル的にsetTimeoutで再現 setTimeout(()=>{ if(a <= 0) { //エラーにしたい場合はrejectに結果を渡す reject(a + "はマイナスの数"); }else { //普通の場合はresolveに結果を渡す resolve(a); } },1000) }); } const main = ()=>{ osoiProc(5).then(res=>{ console.log(res); }).catch(err=>{ console.log(err); }); osoiProc(-5).then(res=>{ console.log(res); }).catch(err=>{ console.log(err); }); console.log("終了"); } main();
osoiProcが非同期処理の例
Promiseを使っている。
Promiseを使うと、axiosのようにthen等でコールバックを設定し、非同期処理ができる
このコードの場合の結果は以下のようになる
終了
15
-15はマイナスの数
自前の非同期処理でもasync await
promiseを使った非同期処理を呼び出すときにawaitをつければ同じ様に同期処理になる
//非同期な処理をする関数 const osoiProc = (n) =>{ //非同期したい処理はPromiseでくるむ感じ return new Promise((resolve,reject) =>{ let a = 3 * n; //時間が遅くなっているのはサンプル的にsetTimeoutで再現 setTimeout(()=>{ if(a <= 0) { //エラーにしたい場合はrejectに結果を渡す reject(a + "はマイナスの数"); }else { //普通の場合はresolveに結果を渡す resolve(a); } },1000) }); } const main = async ()=>{ try{ let res = await osoiProc(5) console.log(res); }catch(err) { console.log("キャッチされた",err); } try{ let res = await osoiProc(-5) console.log(res); }catch(err) { console.log("キャッチされた",err); } console.log("終了") } main();
osoiProcは変更なく、呼び出している方が変わる
then()に渡すコールバック関数で取得できる引数は、await付きのメソッドから返される戻り地になる。
エラー時に呼び出されるcatch()のコールバック関数に相当するのは
普通のtry catch構文で対応できるようになっている。
実行結果はこのようになる
15
キャッチされた -15はマイナスの数
終了
コールバックがなくなり、非常にわかりやすい処理になる。
まとめ
- thenで非同期になっている関数を同期したい場合は、呼び出し時にawaitをつける
- awaitが登場する関数にasyncをつける
- 非同期で処理される関数には特に変更を加えることはない