はじめまして!!ノベルワークス2年目、駆け出しエンジニアのそうまです!!
今回は async/await
を利用したコードの実行順序について整理しました!
調査理由
async/await
を使って呼び出した関数内でもさらに async/await
を使用している場合、コードの実行順序を頭で整理出来なくなってしまったため、今一度纏めようと思いました!!
パターン1
呼び出した関数内で async/await
を使用しなかった場合。
以下サンプルコードです。
const getSampleData = async(type: "users" | "comments") => {
const response = await fetch(`https://jsonplaceholder.typicode.com/${type}`)
return await response.json();
}
const request = async() => {
// データ取得が完了したら、ログを出力
getSampleData("users").then(() => {
console.log("ユーザーデータ取得完了")
})
// データ取得が完了したら、ログを出力
getSampleData("comments").then(() => {
console.log("コメントデータ取得完了")
})
}
const run = async() => {
await request()
console.log("通過")
}
run()
この場合ログの出力順はどうなるでしょうか?
僕は「run
関数内で await
を使用しているんだし "通過" のログが一番最後に出る筈」だと思ってしまってました...。
しかし実際には以下のような順番になります!
- "通過" のログ出力
- "ユーザーデータ取得完了"のログ出力
- "コメントデータ取得完了"のログ出力
当たり前っちゃ当たり前なんですが...確かに run
関数内で await
を使用して request
関数を呼び出してはいますが、request
関数内の処理はレスポンスを待ちません。(正確には json
のパース)
つまり、request
関数内の getSampleData
の返す Promise
が pending
(待機中) の状態で、request
関数の返す Promise
が fulfilled
(履行済み) になってしまうので "通過" のログ出力が先に実行されると解釈しています!
パターン2
呼び出した関数内で 一部 async/await
を使用した場合。
以下サンプルコードです。
(request
関数の最初の getSampleData
の呼び出しに await
を付けました)
const getSampleData = async(type: "users" | "comments") => {
const response = await fetch(`https://jsonplaceholder.typicode.com/${type}`)
return await response.json();
}
const request = async() => {
// データ取得が完了したら、ログを出力
await getSampleData("users").then(() => {
console.log("ユーザーデータ取得完了")
})
// データ取得が完了したら、ログを出力
getSampleData("comments").then(() => {
console.log("コメントデータ取得完了")
})
}
const run = async() => {
await request()
console.log("通過")
}
run()
この場合はどうなるでしょうか?
...そうですね!request
関数は最初の getSampleData
関数のレスポンスを待つので以下のような実行順になります!
- "ユーザーデータ取得完了"のログ出力
- "通過" のログ出力
- "コメントデータ取得完了"のログ出力
パターン3
今ままでを踏まえて、以下のようなコードの時実行順はどうなるか考えてみましょう。
const getSampleData = async(type: "users" | "comments" | "posts") => {
const response = await fetch(`https://jsonplaceholder.typicode.com/${type}`)
return await response.json();
}
const request = async() => {
await getSampleData("users").then(() => {
console.log("ユーザーデータ取得完了")
})
await getSampleData("comments").then(() => {
console.log("コメントデータ取得完了")
})
}
const postRequest = async() => {
await getSampleData("posts").then(() => {
console.log("ポスト完了")
})
}
const run = async() => {
postRequest()
request()
}
run()
この場合は request
関数内の処理はコードの記載順に実行されますが、postRequest
内の処理と request
関数内の処理は並列実行となります。つまり、"ユーザーデータ取得完了"ログ と "コメントデータ取得完了"ログ は順番通りに出力されますが、"ポスト完了"ログ だけはどこで出力されるかわからない状態となります!!
纏め
なんとなくasync/await
を用いた非同期処理の実行順について理解を深められたかなと思います!
ただ、以下でも書いたんですが、やはり javascript
で書いたコードがブラウザでどういう仕組みで動いているのかを理解したいので、次回こそは「イベントループ」や「マイクロタスク」あたりの調査を進めていきたいです!