はじめに
会社の同期から、Javascriptの非同期処理について、「async/awaitとthenでどっちを使ったほうがいいのだろうか?」と聞かれて、基本はasync/awaitでよいけど、thenの方が良い場合もあるなーと思ったので、調査、整理してみました。
async/awaitが優れている点について
async/awaitはES2016で追加された文法なので、それまでのthenが抱えていた課題を改善、解決する、非同期処理の記述が可能になります。
具体的には以下のようなメリットが有ると思っています(個人的意見)
- 複数の非同期処理について、依存関係が発生してくる場合に書きやすい
- エラーハンドリングが見やすい
- Javascriptエンジンによっては早くなる
- thenを使ったときと比べてネストが発生しない
一つ一つについてコメントしたいと思います。
非同期処理について、複雑に依存関係がある場合に書きやすい
async/awaitを使用すると、非同期処理に依存関係は発生している場合効果を発揮してくると思っています。
以下のように、同期処理を記載するとほとんど同じ書き味で書くことができます。
const asyncFunc = async () => { const getUserId = () => Promise.resolve(...) // APIを叩いてuserIdを取得する関数 const getGroup = (userId) => Promise.resolve(...) // APIを叩いて、Promiseでラップされた引数で与えられたユーザーが所属するgroup情報を取得する関数 const getDepartment = (userId) => Promise.resolve(...) // APIを叩いて、Promiseでラップされた引数で与えられたユーザーが所属するdepartment情報を取得する関数 const userId = await getUserId() const groupId = await getGroup(userId) console.log(groupId) const departmentId = await getDepartment(userId) console.log(departmentId) }
一方、これをthenで書こうとすると、次のように書くことができます。
Promise.allを使用したり、ネストが発生したり、async/awaitと比べると見づらいコードになっているかなーと感じます。
const asyncFunc = () => { const getUserId = () => Promise.resolve(...) // APIを叩いてuserIdを取得する関数 const getGroup = (userId) => Promise.resolve(...) // APIを叩いて、Promiseでラップされた引数で与えられたユーザーが所属するgroup情報を取得する関数 const getDepartment = (userId) => Promise.resolve(...) // APIを叩いて、Promiseでラップされた引数で与えられたユーザーが所属するdepartment情報を取得する関数 getUserId().then(userId => { Promise.all([getGroup(userId), getDepartment(userId)]).then((values) => { values.map(value => console.log(value)) } }) }
エラーハンドリングが見やすい
JavaやC++と同様のtry/catchが使えるので、エラーハンドリングが見やすいです。
一方、then/catchはメソッドチェーンになるので、catchを書き忘れていても気づかずスルーかもしれないなーと感じますので、try/catchを使用することでそのようなミスも減りそうかなと思います(慣れの問題かもしれませんが)
Javascriptエンジンによっては早くなる
awaitについてJavascriptエンジンごとに独自の実装がされているので、Javascriptエンジンによる、パフォーマンスチューニングが可能なようです。
例えば、V8ではPromiseをそのまま使って処理したときよりも早くなるようです。
thenを使ったときと比べてネストが発生しない
ここはawaitの説明でもよく出てきますが、個人的には一つネストするくらいなら別に可読性は落ちないんじゃないかなーと考えています。
また、map関数もあったりするし、引数で匿名関数を渡すケースはJSにおいて普通にあることにも思えるので、このくらいいいのではないかなと思っています。
thenを使うとき
今まで、async/awaitの素晴らしさを書いたわけですが、以下のようなケースでは、thenを使ったほうがいいんじゃないかなーと個人的には思っています。
- Promiseの後処理がかなり単純な場合
- 例
- fetch APIのresponseを取るとき
- 例
また、以下のケースは問答無用でthenを使わなければならないです。
- async funcの中以外で非同期処理を書く場合
- 関数の中に書いていない場合(グローバル変数など)
- 関数について、何らかの制約があり、asyncキーワードがかけない場合
- ES5以前を使用している場合
それでは、個人的に考えているthenを使ったほうがいいケースについて書いていきます
Promiseの後処理がかなり単純な場合
例を見てみていただくのが一番わかり易いと思うので、書いてみます。
無駄な変数を定義しなくて良い分、読みやすく、意図もはっきりしたコードになっていると思います。
例 fetch APIやaxiosを使って取得したデータから必要な情報のみを取得する場合
thenで書いた場合
// Fetch APIを使った場合 const userId = fetch('http://example.com/api/user') .then(response => response.json()) .then(data => data.id) // axiosを使った場合 const groupId = axios.get('http://example.com/api/group') .then(response => response.data.id)
async/awaitで書いた場合
// Fetch APIを使った場合 const response = await fetch('http://example.com/api/user') const user = await response.json() const userId = user.id // axiosを使った場合 const group = axios.get('http://example.com/api/group') const groupId = group.data.id
まとめ
async/awaitとthen、使うならasync/awaitだが、thenで書いたほうがいい場合もある(個人的な見解です)
みなさんもご意見あれば、コメントいただけると嬉しいです!