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をつける
- 非同期で処理される関数には特に変更を加えることはない