coとthunkifyで書いていた非同期処理のフローをasync/awaitで書き換える
要約
タイトルの通りasync/awaitで書き換え可能だしAsync Functionはアロー関数でも定義できるしいい感じです。
coとthunkifyではどうだったか
Node.jsアプリケーションを書いていて、非同期処理の逐次実行が必要なとき、今まではcoとthunkifyを使って書いていた。 具体的にどんな場面だったかというと、requestモジュールを使ってAPIを叩き、その結果を使って別のAPIを叩き……みたいな状況だ。 従来は、Node.jsでの非同期処理を、coとthunkifyを使って幸せに書こうを参考に、以下のようなコードを書いていた。
const co = require('co'); const thunkify = require('thunkify'); const request = thunkify(require('request')); function doSomething() { const headers = { 'Content-Type': 'Application/json' } co(function* (){ const option1 = { headers, url: 'http://example.com/api/first' }; const reqponse1 = yield request(option1); const option2 = { headers, url: 'http://example.com/api/second', method: 'POST', body: JSON.stringify({ state: response1[1].state }) }; const response2 = yield request(option2); }).catch((err) => console.log(err)); }
thunkifyによってthunkに変換されたrequestをcoがPromiseに変換し、ジェネレータとの組み合わせによってAPIリクエストのレスポンスを待つことができるようになっている。 このように言語化するとthunkifyやcoのやっていることが余計に感じられ、素のgeneratorとPromiseでもいいじゃんと思われた。そこで、せっかくなのでES2017で追加されたasync/awaitを使って書き直してみた。
async/awaitによる書き換え
const request = require('request-promise-native'); async function doSomething() { const headers = { 'Content-Type': 'Application/json' }; try { const option1 = { headers, url: 'http://example.com/api/first' }; const responseBody1 = await request(option1); const option2 = { headers, url: 'http://example.com/api/second', method: 'post', body: JSON.stringify({ state: responseBody1.state }) }; const responseBody2 = await request(option2); } catch (err) { console.log(err); } }
requestの代わりにrequest-promise-nativeを使う。request-promise-nativeのrequest()はPromiseを返し、await式によってPromiseがresolveされるまでdoSomething()の実行は停止される。Promiseがrejectされたら外のtry-catchで捕捉できる。 多少見通しがよくなったぞと思って改めてMDNのAsync Function の解説を見ると、
async/await 関数の目的は、 promise を同期的に使用する動作を簡素化し、 Promise のグループに対して何らかの動作を実行することです。 Promise が構造化コールバックに似ているのと同様に、 async/await はジェネレータと promise を組み合わせたものに似ています。
とあり、あっハイその通りですといった記載があった。
Async FunctionをExpressのルーティングに使う
Async FunctionをExpressのルーティングのハンドラーに使うと、リクエストを起点に非同期処理を順番に実行するといったコードがシンプルになる。 これは上記のdoSomething()のようにAPIを順番に叩く例。
const express = require('express'); const router = express.Router(); const request = require('request-promise-native'); router.get('/something', async (req, res) => { const headers = { 'Content-Type': 'Application/json' } try { const option1 = { headers, url: 'http://example.com/api/first' }; const responseBody1 = await request(option1); const option2 = { headers, url: 'http://example.com/api/second', method: 'post', body: JSON.stringify({ state: responseBody1.state }) }; const responseBody2 = await request(option2); res.send(); } catch (err) { console.log(err); res.status(500).send(); } });
アロー関数を使ってasync () => {}
とも書けるのがいい。