はじめに
原理原則はまだまだわかりません。
なので、やってみたor検証的な側面が強いです。
非同期とは
同時に進行せず、独立して実行される。されること
"非同期である"を決定づける要素
- Promseを返す関数
- asyncで定義された関数←asyncで定義された関数は必ずPromiseを返す
- callbackを受け取る関数: 省略
async/awaitをつけた場合とつけない場合
どこかのwebサーバーにrequestを送る処理を想定する
// 何もつけない
const doRequest1 = () => {
const res = axios.get("https://example.com")
console.log("doRequeset1: I got response")
return res
}
// asyncとawaitをつける
const doRequest2 = async () => {
const res = await axios.get("https://example.com")
console.log("doRequest2: I got response")
return res
}
何が違うの?
- await以降の処理の実行タイミングが違う
- doRequest1の場合は、requestが実行された直後にconsole.log()が実行される
- doRequest2の場合は、requestのresponseが来た後にconsole.log()が実行される
※別の角度から捉えると、doReqeust1はaxiosのPromiseを返してお役目を終了している。
引っかかりやすいケース
res1 = doRequest1()
res2 = doRquest2()
console.log("A")
console.log("B")
console.log("C")
これは両方のパターンのログもいつ出てくるかわからない
asyncとawaitで囲ったからといってdoRequest2()の処理は同期的にならない
では、doRequest2は裏で何をしているか?
axiosでexmaple.comにrequestするで
→ ん、awaitついとるやん。一旦、Promiseを返しとくわ
→ (get request完了)
→ よっしゃ続きの処理やるか、logを出力して
→ resをreturnね。さっき返したPromiseに渡しとくわ
つまり、res2の面倒を最後まで見ることができるようになっているだけ。
※ res1の方はPromiseのobjectを作り出して処理を終了している
responseを受け取ってから処理をしたい
その場合は以下の様になる
const main = async () => {
res2 = await doRequest2()
console.log("A")
console.log("B")
console.log("C")
}
※ awaitを使うにはtopレベルでasyncによりwarpする必要がある
asyncとawaitをつけるメリット
エラーハンドリングができるようになる
const doRequest1 = () => {
try {
const res = axios.get("https://example.com")
console.log("doRequeset1: I got response")
return res
} catch (error) {
console.log("Error in doRequest1")
}
}
これはrequestのerrorを捉えれない。
doReqeust1()自体はaxiosのPromiseを一瞬で返して処理は終了されるため
const doRequest2 = async () => {
try {
const res = await axios.get("https://example.com")
console.log("doRequeset2: I got response")
return res
} catch (error) {
console.log("Error in doRequest2")
}
}
これはOK。
awaitを書くことで、Promiseの完了を関数内で捉えることができる。
よって、resquest中におきたerrorは関数内のtry-catchで補足される
テストができる
describe('test doRequest1', () => {
it('get response successfully', () => {
res1 = doRequest1()
});
});
これは絶対成功する。
不適切なrequestをしたとしても。
axiosのrequest処理の完了を待たずしてtestが終了するため
describe('test doRequest2', () => {
it('get response successfully', async () => {
await doRequest2()
});
});
これは大丈夫。
- request処理の終了が終わってからテストが終了するため
- テストフレームワークが
async関数の場合、
返されたPromiseの解決を待ってからテスト完了とするため。
awaitのもう一つの役目
Promiseの開封作業をしている
const res2 = await doRequest2() // Promise<fulfiled>の中身を開封してresに突っ込む
// 同じことをやろうとすると
const Promise1 = doRequest1()
Promise1.then( response => { // resopnseはPromise<fulfiled>の中身。名前は何でもOK
const res1 = response
})
callback地獄のsample
// 複数のリクエストがある場合...
doRequest1()
.then(() => {
return doRequest2()
})
.then(() => {
return doRequest3()
})
.then(() => {
doSomething1();
doSomething2();
doSomething3();
})
await doRequest1()
await doRequest2()
await doRequest3()
doSomething1(); // 全部完了後に実行
doSomething2(); // 全部完了後に実行
doSomething3(); // 全部完了後に実行
感想
また学んだことがあれば追記していく予定です。
とりあえず最低限のポイントは理解できたと思ってます。