JavaScript
HameeDay 19

promiseそしてgeneratorからのasync awaitへ

はじめに

この記事はHameeアドベントカレンダー19日目の記事です。
https://qiita.com/advent-calendar/2017/hamee

firebaseのデータの取得をする上でthenチェーン地獄を経験し、generatorそして
async awaitまで学んだのですごくザックリとまとめてみました。
promiseを知らない方はこちらがとても参考になりましたので、
読んでみてください。
JavaScript Promiseの本

firebaseでのデータ取得

firebaseなどの非同期リスナー型で値を取得しさらにその取得した値をもとに再度値を取得なんてやってると下記のようにずらずらとthenチェーン地獄になってしまう。
firebaseでのデータ取得方法は割愛しますのでドキュメント読んでください!
*firebaseはpromiseオブジェクトを返してくれる。

thenチェーン地獄

thenチェーン地獄
asyncA()
 .then((asyncA_value) => {
    return asyncB(asyncA_value)
 }).then((asyncB_value) => {
    return asyncC(asyncB_value)
 }).then((asyncC_value) => {
    return asyncD(asyncC_value)
 }).then((asyncD_value) => {
    return asyncE(asyncD_value) 
 }).then((asyncE_value) => {
    console.log(asyncE_value)
 })

ぐぬぬ。チェーン化できるのは良いが見づらい。なんとかしたい。

generatorとco

そこでES6で導入されたgenerator関数とcoというライブラリを使うとthenチェーン地獄は以下のように記述することができます。

generator
const co = require('co')
co.wrap(function* () {
 const asyncA_value = yield asyncA()
 const asyncB_value = yield asyncB(asyncA_value)
 const asyncC_value = yield asyncC(asyncB_value)
 const asyncD_value = yield asyncD(asyncC_value)
 const asyncE_value = yield asyncE(asyncD_value)
 console.log(asyncE_value)
})

とっても読みやすい!
yieldを使うとgenerator関数の実行をpromiseの解決を待ち、解決された値(reject or resolve)を返すまで一時停止してくれるので、for文や条件判定文なども同期的に記述することができます。
しかし、非同期構文の進化はこれで終わりではありません。

async/awaitの登場

なんとES7よりasync/awaitという非同期構文のシンタックスシュガーが実装されました。
このasync関数とawait演算子を使用するとcoライブラリでwrapしてgenerator関数を使った際の挙動と同じように、await演算子によりasync functionの実行を一時停止し、promise の解決を待ち、その後、async function の実行を再開し、解決された値を返してくれます。
*await演算子はasync関数内でしか使えません。

async_function
async sample () {
 const asyncA_value = await asyncA()
 const asyncB_value = await asyncB(asyncA_value)
 const asyncC_value = await asyncC(asyncB_value)
 const asyncD_value = await asyncD(asyncC_value)
 const asyncE_value = await asyncE(asyncD_value)
 console.log(asyncE_value)
}

またpromiseがrejectされた場合、await演算子は理由となった値をスローしてくれます。
なので以下のような記述で呼び出し元はエラーをキャッチすることができます。

await/catchパターン
const async_value = await sample().catch(err => {
 console.log(err)
})

おわりに

いかがでしたでしょうか?
thenチェーン地獄から今はasync awaitまでなんとかたどり着き、楽しく非同期処理を扱えてます。
JSはReactNativeやlambda functionで使えるし、人気のある言語なのでこれからはより深く学んでいこうと思います。
ご一読ありがとうございました!

参考サイト

async関数においてtry/catchではなくawait/catchパターンを活用する