Async/Await の非同期処理をジェネレータを使って再現してみた

# 0.はじめに
今回はECMAScript 6から導入されたジェネレータを使ってAsync/Awaitの非同期処理を再現してみたいと思います。
これをやることで非同期処理がどう動いているかやジェネレータの動きの理解が少しでも深まれば良いなと思い勉強がてらやってみました。

ジェネレータやAsync/Awaitの説明は他に書いている方がたくさんいるので割愛します。

早速コードを書いていきます

# 1.Async/Awaitをまずは使った場合
```js
async function getTweets(userName) {
  console.log('Me first')
  const data = await fetch(`http://twitter.com/${userName}`)
  console.log(data)
}
getTweets('oliver')
console.log('Me second')
```
このコードの説明を軽くすると、`getTweets`関数の引数にユーザー名を渡すとそのユーザーが投稿したTweetsが取得できるというシンプルなもの。
注目すべきは`await fetch()`の部分でFetch APIを使ってHTTPリクエストを行い、awaitを使ってPromiseの値が取り出されるまで待っています。
無事ツイートが取得できたらその下の`console.log(data)`でレスポンスを出力しています。

# 2.ジェネレータを使った場合
```js
function doWhenDataFetched (data) {
  returnElement.next(data)
}

function* getTweets(userName) {
  const data = yield fetch(`http://twitter.com/${userName}`)
  console.log(data)
}

const returnElement = getTweets('oliver')
const futureData = returnElement().next()
futureData.then(doWhenDataReceived)
```
Async/Awaitを使った時より関数が一つ増えコード量が増しました。
まず、最初に先ほどと同様`getTweets`でツイートを取得します。
ですが、今回は`getTweets`内でawaitはしておりません。

では、どうやってレスポンスが返ってくるのを待ち、その下の`console.log(data)`でFetch APIで返ってきたデータを出力するのでしょうか。

ポイントとなるのは`getTweets`前にある`*`と`fetch()`前にある`yield`です。
この`*`を書くことでジェネレータ関数を定義し、`yield`キーワードを使ってジェネレータの処理を一時停止しています。
`yield`によって`await`行ったPromiseの値が取り出されるまで待つを再現しています。

では順番にコード追っていきます。

```js
const returnElement = getTweets('oliver')
```
ここではジェネレータ関数であるgetTweetsからジェネレータ(ジェネレータ関数から取得できるオブジェクト)を作り、それを定数returnElementにセットします。
注意すべき点は、ここでは`getTweets`がまだ実行されていないこと。ただ、ジェネレータを作っただけです。

```js
const futureData = returnElement().next()
```
ここで先ほど作ったジェネレータを使って実際にジェネレータ関数(元の名前getTweets)を実行します。

ここで理解しておく必要がしておきたいのは
・ジェネレータはイテレータを持つオブジェクト
・このオブジェクトは`next()`メソッドをもつ
・`next()`を実行すると`yield`にぶつかるまで関数内の処理が行われる
・`next()`を実行するとイテレータリゾルトを返す
・再度関数を実行すると前回`yield`したところから処理が再開される
ということ。

これを理解した上でコードを読むと`returnElement().next()`を行い関数を実行していることがわかります。

```js
function* getTweets(userName) {
  const data = yield fetch(`http://twitter.com/${userName}`)
  console.log(data)
}
const returnElement = getTweets('oliver')
const futureData = returnElement().next()
```
ついに関数が実行されました。
いきなり`yield`が登場し、そこでfetchが行われています。
`yield`がきたので以下のことがおきます
・処理が一時停止
・イテレータリゾルトを返す

ということは`returnElement().next()`からイテレータリゾルトが返され、定数`futureData`にセットされます。
この時点で`futureData`はfetchが返すPromiseであることを頭に入れておいてください。
この段階では、Async/Awaitのように
fetchがresolveもしくはrejectするのを待つこと、その下にある`console.log(data)`がまだ実行しないということができました。

あとは、fetchがツイートを取ってきたらコンソールにデータを出力する処理ができたら再現完了です。

```js
futureData.then(doWhenDataReceived)
```
futureDataはPromiseであるので`.then()`を使ってresolveの場合に実行したい処理をセットします。
このresolveの場合に実行したい処理がこちらです。

```js
function doWhenDataFetched (data) {
  returnElement.next(data)
}
```
この関数内では再度`next()`が登場しています。
ジェネレータ関数である`returnElement`を`next()`で実行しています。
ポイントは

・ジェネレータ関数は再度`next()`で実行すると前回処理を中断した位置から、処理が再開されるということ
・next()の引数にfetchで取得したdataは渡していること

です。

`next()`に引数を渡すと、その値をジェネレータに渡すことができます。
そのため下記のコードを再度見ると

```js
function* getTweets(userName) {
  const data = yield fetch(`http://twitter.com/${userName}`) <===　ここから再開
  console.log(data)
}
```
引数で渡した`data`はこのジェネレータ関数内で定義した定数`data`にセットされます。
そしてその下でコンソールに取得した`data`が出力されます。

# 3.終わりに
いかがでしたか？
ジェネレータを使ってAsync/Awaitを再現しましたが、結構コード量が増え、なんとなく処理が複雑になった気がします。
Async/Awaitって便利ですね(安易な結論ですが。。)
これを行うことでAsync/Awaitの動きジェネレータの仕組みや動作の理解がほんの少しは深まった気がします。