JavaScriptのgeneratorについて part2

# 初めに
今回は`[Symbol.asyncIterator]`、非同期ジェネレータについてまとめていきたいと思います。

参考文章はこちらです。
[Async iteration and generators - javascript.info](https://javascript.info/async-iterators-generators)
[Symbol.asyncIterator - MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/asyncIterator)

# [Symbol.asyncIterator]
```jsx
let range = {
  from: 1,
  to: 5,

  [Symbol.asyncIterator]() {
    return {
      current: this.from,
      last: this.to,

      async next() {
        await new Promise(resolve => setTimeout(resolve, 1000));

        if (this.current <= this.last) {
          return { done: false, value: this.current++ };
        } else {
          return { done: true };
        }
      }
    };
  }
};

// IIFE
(async () => {
  for await (let value of range) {
    console.log(value)
  };
})()
```
参考文章からの例です。最近の勉強では初見の書き方が出てきたり、既定の書き方で書かねば動かないというのがよくあるので、下調べしてから自分なりに解釈してメモします。
今回の`[Symbol.asyncIterator]`の非同期反復処理も既定の書き方で、
- 中身は`next()`メソッドを内蔵するオブジェクトを返す。
- `next()`では`Promise`オブジェクトを返す。あるいは`async next()`で返すオブジェクトを`Promise`に保証する。

自分のテストでは二番目が`next()`から`async`と`await`を除いてもちゃんと動けますが、しかし非同期という目的が実現できません。ここは`await`で`next()`毎回の反復処理が`new Promise(resolve => setTimeout(resolve, 1000))`が終わるまで待ってから、下のコードを進むのです。

[Using javascript's Symbol.asyncIterator with for await of loop - stackoverflow](https://stackoverflow.com/questions/55531247/using-javascripts-symbol-asynciterator-with-for-await-of-loop)を参考して、`next()`を使わないように書いてみました。
```jsx
// generator is iterable object
async function* counter() {
  for (let i = this.from; i <= this.to; i++) {
    await new Promise(resolve => setTimeout(resolve, 1000));
    yield i
  }
};

let range = {
  from: 1,
  to: 5,
  [Symbol.asyncIterator]: counter
};

async function test() {
  for await (let value of range) {
    console.log(value)
  }
};

test()
```
- `[Symbol.asyncIterator]`には反復可能のオブジェクト。（`[Symbol.asyncIterator](){}`の書き方ならオブジェクト返す関数にする。）
- 非同期の実現は`async generator`で`await`と`setTimeout()`などの遅延処理メソッドと併用し、そして`async function`で`for await (let item of iterable)`で結果をコンソール。

`[Symbol.asyncIterator]`にはオブジェクト、`async generator`には`yield`で値の転送、`async function`に`for await (let item of iterable)`でジェネレータからのプロミスオブジェクトを待つ。
...いろいろと書き直してみたいけど、書き方が限られてる気がします。

下は参考文章からの書き方です。
```jsx
let range = {
  from: 1,
  to: 5,

  async *[Symbol.asyncIterator]() {
    for (let value = this.from; value <= this.to; value++) {
      await new Promise(resolve => setTimeout(resolve, 1000))

      yield value
    }
  }
};

(async () => {
  for await (let value of range) {
    console.log(value)
  }
})()
```
`async *[Symbol.asyncIterator](){}`で`for`ループを包んで、`await`で遅延処理を待ち、`yield`で値を返す。下の`IIFE`でプロミスオブジェクト返すのを待って値をコンソール。
こちらのほうが読みやすい書きやすくと思います。

# real-life example: paginated data
これもまた参考文章からの例です。
```jsx
async function* fetchCommits(repo) {
  let url = `https://api.github.com/repos/${repo}/commits`

  while (url) {
    const response = await fetch(url, {
      headers: { 'User-Agent': 'Our script' }
    })

    const body = await response.json()

    let nextPage = response.headers.get('Link').match(/<(.*?)>; rel="next"/)

    nextPage = nextPage?.[1]

    url = nextPage

    for (let commit of body) {
      yield commit
    }
  }
};

(async () => {

  let count = 0

  for await (const commit of fetchCommits('javascript-tutorial/en.javascript.info')) {

    // console.log(commit.author)
    console.log(commit.author.login)

    if (++count == 10) {
      break
    }
  }
})()
```
全体のプロセスとしては、
`url`確定
↓
`fetch()`から返されたレスポンスを待つ
↓
レスポンスをJSONにパースしたり、
ページ作りのためレスポンスから[`Headers.get()`](https://developer.mozilla.org/ja/docs/Web/API/Headers/get#%E4%BE%8B)を使い、キーでLinkオブジェクトを選択し、中に`/<(.*?)>; rel="next"/`に一致した値を取り出し`nextPage`へ。
`nextPage`が存在したら（`null/undefined`でなければ）、[`Cache.match()`](https://developer.mozilla.org/ja/docs/Web/API/Cache/match)で返された`Promise`（ここは配列）インデックス[1]のリンクを取り`url`に書き換えて、全部が終わったら次の`while`ループへ。
↓
レスポンスの全体からオブジェクト単位`yield`経由で値を転送し、キーで特定範囲の文字列を取り出す。（ここはコンソール、ほかにHTMLに戻すとかも。）
1ページの表示上限を設定する。

# これまでの感想やまとめ
APIドキュメントを参考しながら、要求に応じたresponseをJSONにパースしJSONから一定の規律を見つけて、処理の仕方やHTMLへどう表現していくを考えるとか、一定の順序で進めば難しくないですが。
以前も`require()`でレスポンスからheaderやstateCodeなど取得して、それに対応するコードを実践したことがあるけれど、最近勉強メモの文章を書きながらやはり自分がHTTPリクエストへの認識が浅はかだなあと思います。
例えばrequestの処理の違いと、headerに注意すべきところ（'User-Agent'が偽造できる問題とか）、CORSを利用してCSRF/XSS防止するなど、今の自分は基礎どころか入門レベルと思います。
より安全なWeb Appを作るためにこれからJavaScript以外、ネットワーク特にHTTP関連についてまとめていきたいと思います。