JavaScriptのasyncIteratorについて

# 初めに
非同期関数やイテレーションの最終パートです。今回は`[Symbol.asyncIterator]`、`for await...of`、`asyncGenerator`についてまとめてみました。

イテレータの基本概念、前の文章にまとめてみました。

https://qiita.com/hu-yu/items/4fa1e00991a6f6141c55

非同期関数の基本概念や、動きなどはこちらです。

https://qiita.com/hu-yu/items/2bd2d6e2c5b5f60c33e7

https://qiita.com/hu-yu/items/54b767ea002d5489fdfd

今回の参考文章はこちらです。

https://es6.ruanyifeng.com/?search=asyncIterator&x=2&y=15#docs/async-iterator

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Iteration_protocols#the_async_iterator_and_async_iterable_protocols

# Asynchronous iterator - `[Symbol.asyncIterator]`
組み込みの仕方や使い方では`[Symbol.iterator]`とほぼ同じです。`[Symbol.asyncIterator]`プロパティだけを持つ場合は`for await...of`でイテレータしなければならない。

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/asyncIterator

## Syntax
```jsx
// set [Symbol.asyncIterator] property
iterableData[Symbol.asyncIterator] = async function* something() {...}
iterableData[Symbol.asyncIterator] = async function* () {...}
// or inside of iterable Data
async*[Symbol.asyncIterator]() {...}

// call [Symbol.asyncIterator]
async function something(iterableData) {
  for await (let item of iterableData) {
  ...
  }
}
```
非同期ジェネレータを動かすには必ずイテレータできるデータ構造か、データを生成する`[Symbol.asyncIterator]/[Symbol.Iterator]`プロパティを持たねばなりません。そしてプロパティを`for await...of`非同期に呼び出すには`async function`が必要です。
```jsx
// @@asyncIterator
let arr = [1, 2, 3];
arr[Symbol.asyncIterator] = async function* () {
  for (let i = 0; i < this.length; i++) {
    yield this[i];
  }
};
async function useForAwait(arr) {
  console.log('start for await');
  // for await...of call @@asyncIterator => [Symbol.asyncIterator]
  for await (let item of arr) {
    console.log(item)
  }
  console.log('end for await');
}
console.log('start');
useForAwait(arr);
console.log('end');
// start
// start for await
// end
// 1
// 2
// 3
// end for await
```
forループは同調に動きますが、`await`で`Promise`のように非同期に処理し`Microtask Queue`に移行されて、`Call Stack`がクリアになってから`Microtask Queue`から古い順番から実行していくのです。
前の文章では`await`や`Microtask Queue`の動きなどまとめてみたので、ご参考になれば嬉しいです。

https://qiita.com/hu-yu/items/54b767ea002d5489fdfd#await



# call `[Symbol.asyncIterator]` vs. `[Symbol.iterator]`
ここから検証に入りたいと思います。
普通の関数に`for...of`、非同期関数に`for...of`、非同期関数に`for await...of`では、一体どちらのプロパティを呼び出すのか、そして動きではどこか違うかを知りたいです。
```jsx
// for...of in function
function controlledFn(iterable) {
  console.log('for...of in controlledFn start');
  for (let item of iterable) {
    console.log(item);
  }
  console.log('for...of in controlledFn end');
}
// for...of in async function
async function useIterator(iterable) {
  console.log('for...of in useIterator start');
  for (let item of iterable) {
    console.log(item);
  }
  console.log('for...of in useIterator end');
}
// await for...of in async function
async function useAsyncIterator(iterable) {
  console.log('await for...of in async function start');
  for await (let item of iterable) {
    console.log(item)
  }
  console.log('await for...of in async function end');
}
```
まずは普通に`[Symbol.iterator]`だけ持っている配列を用いて、別々結果を見ていきたいと思います。
```jsx
// iterable with [Symbol.iterator] property
let arr = [1, 2, 3];
// result
console.log('test start');
controlledFn(arr);
console.log('test end');
// test start
// for...of in controlledFn start
// 1
// 2
// 3
// for...of in controlledFn end
// test end
//
console.log('test start');
useIterator(arr);
console.log('test end');
// test start
// for...of in useIterator start
// 1
// 2
// 3
// for...of in useIterator end
// test end
//
console.log('test start');
useAsyncIterator(arr);
console.log('test end');
// test start
// await for...of in async function start
// test end
// 1
// 2
// 3
// await for...of in async function end
```
`[Symbol.iterator]`だけ持っている場合は、`for await...of`もちゃんと作用している、というより普通の`for...of`に`await`を入れて本来の働きをもたらしただけだと思います。`[Symbol.iterator]`持ちの`iterable`であれば`for await...of`も利用できます。

`[Symbol.asyncIterator]`プロパティを追加したらどうなるでしょう。
```jsx
// iterable add [Symbol.AsyncIterator] property
arr[Symbol.asyncIterator] = async function* () {
  for (let i = 0; i < this.length; i++) {
    yield this[i];
  }
};
// result
console.log('test start');
controlledFn(arr);
console.log('test end');
// test start
// for...of in controlledFn start
// 1
// 2
// 3
// for...of in controlledFn end
// test end
//
console.log('test start');
useIterator(arr);
console.log('test end');
// test start
// for...of in useIterator start
// 1
// 2
// 3
// for...of in useIterator end
// test end
//
console.log('test start');
useAsyncIterator(arr);
console.log('test end');
// test start
// await for...of in async function start
// test end
// 1
// 2
// 3
// await for...of in async function end
```
結果は変わらなかった、と思いながら`[Symbol.iterator]`を消したらどうなるでしょう。
```jsx
// only [Symbol.AsyncIterator]
arr[Symbol.iterator] = null;
arr[Symbol.asyncIterator] = async function* () {
  for (let i = 0; i < this.length; i++) {
    yield this[i];
  }
};
// controlledFn(arr); // TypeError: iterable is not iterable
// useIterator(arr); // TypeError: iterable is not iterable
console.log('test start');
useAsyncIterator(arr);
console.log('test end');
// test start
// await for...of in async function start
// test start
// 1
// 2
// 3
// await for...of in async function end
/**
 * [Symbol.iterator] => for...of/for await...of
 * [Symbol.iterator] & [Symbol.AsyncIterator] => for...of/for await...of
 * only [Symbol.AsyncIterator] => for await...of
*/
```
`[Symbol.AsyncIterator]`プロパティだけ残された場合は`for await...of`でイテレータできるが、`for...of`ではできません。
これで`for await...of`の使用では`[Symbol.AsyncIterator]`プロパティを呼び出すか、`for...of`で`[Symbol.iterator]`を呼び出したうえで`await`の機能が働くとわかりました。

# for await...of
前の検証から`[Symbol.AsyncIterator]`プロパティが持たなくても、`for await...of`は`for...of`で`[Symbol.iterator]`を呼び出してから`await`の機能を加えることがわかった。でもそれって、イテレータは`[Symbol.iterator]`で統合して`await`に任せればいいという話になるのでは？`[Symbol.AsyncIterator]`の存在が曖昧になって使うタイミングがわからなくなってしまう気がする。

なのでここからさらに別の検証に入りたいと思います。
ネストされた反復可能のオブジェクトなら`for...of`か`for await...of`またはほかの処理でどうなるでしょうか。

## nested iterable
まずは`for...of`⇒`for await...of`
```jsx
// nested iterable
let arr = [['a', 'b'], ['c', 'd']];
// for...of => for await...of
async function test(arr) {
  console.log('test start');
  for (let item of arr) {
    console.log('check')
    for await (let element of item) {
      console.log(element)
    }
  }
  console.log('test end');
}
console.log('start');
test(arr);
console.log('end');
// start
// test start
// check // for...of is synchronous
// end // for await...of is asynchronous
// a
// b
// check
// c
// d
// test end
```
次に`for await...of`⇒`for...of`
```jsx
// for await...of => for...of
async function test(arr) {
  console.log('test start');
  for await (let item of arr) {
    console.log('check');
    for (let element of item) {
      console.log(element);
    }
  }
  console.log('test end');
}
console.log('start');
test(arr);
console.log('end');
// start
// test start
// end // for await...of is asynchronous
// check
// a
// b
// check
// c
// d
// test end
```
単純な`for...of`⇒`for await...of`のループなら`for await...of`⇒`for...of`とは同じだったが、コードが複雑になったら中に別の処理を任せるかもしれないので`console.log('check')`を入れてみました。そうするとやはり両者動きの違いが見えてきました。

`for...of`は同期に動作するので、`for...of`⇒`for await...of`なら`console.log('check')`が先に出力して、一度外側に残りの`console.log('end')`を処理してからまた`for await...of`に戻る。

`for await...of`⇒`for...of`のほうが逆でした。まず`console.log('end')`、それから`console.log('check')`を出力した。

当たり前の結果かもしれませんが、**何か必ず先に処理してほしいものがあれば`for...of`⇒`for await...of`のなかに挟む、重要度がいまいちで、あるいは何かを待ってから同期に処理してほしいなら`for await...of`⇒`for...of`に。**

配列のイテレータと出力なら一番簡単なのは`map()`、では`await map()`ならどうなるでしょうか。一緒に見ていきたいと思います。
```jsx
async function test1(arr) {
  console.log('test start');
  // await synchronous result
  await arr.map((item) => {
    for (let element of item) {
      console.log(element);
    }
  })
  console.log('test end');
}
console.log('start');
test1(arr);
console.log('end');
// start
// test start
// a
// b
// c
// d
// end
// test end
```
`await`の後ろに同期に動作する関数であれば、同期関数が先に実行してから`await`が機能します。なぜそうなるのか。`await`はジェネレータ関数の`yield`の概念からのキーワードなので、実際に`await`の動きは、さきに後ろの式（`expression`）が処理してから、前の`await`へ解決されたプロミス（`Settled Promise`）の検定してそのまま通過するか、（式の結果が`Promise`ではなかった場合）暗黙に`Promise.resolve()`呼び出しして処理する。

`for await (let/const variable of iterable) {}`は、`for`ループが形成する前に、`await`が`(let/const variable of iterable) {}`は解決されたプロミスではないから`Microtask`に移行して非同期処理し、メインスレッドに残る`console.log('end')`を`Call Stack`へ処理し終わり、`await`に解決されたプロミスの結果が`for`ループに、そして`aync function`に残りの処理を終わらせるのです。

## nested arrayLike
`[Symbol.iterator]`を組み込まれてないけれどイテレータできる構造を思っている配列風オブジェクトならどうなるのでしょうか。

一度`Array.from()`にしてから`[Symbol.iterator]`を持たせるのが一つだと思います。これでは先の例の配列`for...of => for await...of`と`for await...of`⇒`for...of`とは変わりはありません。
```jsx
// ArrayLike Object with calling `[Symbol.iterator]`
let arrayLikeObj = {
  0: ['a', 'b'],
  1: ['c', 'd'],
  length: 2
};
// for...of => for await...of
async function useArrayLike(arrayLike) {
  console.log('test start');
  // asynchronous
  for (let item of Array.from(arrayLikeObj)) {
    console.log('check');
    for await (let element of item) {
      console.log(element);
    }
  }
  console.log('test end');
}
console.log('start');
useArrayLike(arrayLikeObj);
console.log('end');
// start
// test start
// check
// end
// a
// b
// check
// c
// d
// test end
//
// for await...of => for...of
async function useArrayLike(arrayLike) {
  console.log('test start');
  // asynchronous
  for await (let item of Array.from(arrayLikeObj)) {
    console.log('check');
    for (let element of item) {
      console.log(element);
    }
  }
  console.log('test end');
}
console.log('start');
useArrayLike(arrayLikeObj);
console.log('end');
// start
// test start
// end
// check
// a
// b
// check
// c
// d
// test end
```

もし、`Array.from()`経由して`map()`で`for await...of`で処理したらどうなるでしょう。
```jsx
let arrayLikeObj = {
  0: ['a', 'b'],
  1: ['c', 'd'],
  length: 2
};
async function awaitElement(item) {
  for await (let element of item) {
    console.log(element);
  }
}
// async function
async function asyncFn(arrayLikeObj) {
  console.log('test start');
  // Array.from() => synchronous
  // map() => parallel processing
  // for await...of => asynchronous
  Array.from(arrayLikeObj, (item) => awaitElement(item))
  console.log('test end');
}
console.log('start');
asyncFn(arrayLikeObj);
console.log('end');
// start
// test start
// test end
// end
// a // item = [a, b] => await element: a
// c // item = [c, d] => await element: c
// b
// d
// note: map() makes item in arrayLike be processed in parallel, 
// and await makes element processed asynchronously
```
`Array.from()`は同期関数だけど、出力は第二引数の`map()`に非同期関数`awaitElement()`に任したので、出力する前に外側の`console.log('test end')`、それからもっと外側に`console.log('end')`、メインスレッドに同期処理がない（＝`Call Stack`クリア）のを確認してから、`Microtask Queue`から順番に`Call Stack`に入れて処理していく。

ここでまず`arrayLikeObj`の出力順に注目してください。`a`⇒`c`⇒`b`⇒`d`でした。`map()`が`for`ループより高速処理できるのは、`for`みたいに一回に一つの要素に作用するのではなく、一回に全要素に作用するのです。つまり、
```jsx
// Microtask (1)
  for await (let element of [a, b]) {
    console.log(element);
  }
// console.log(a) => // Microtask Queue (1)

// Microtask (2)
  for await (let element of [c, d]) {
    console.log(element);
  }
// console.log(c) => // Microtask Queue (2)
```
`Microtask`では非同期処理の行いで、先に処理されたもの（Settled Promise）が`Microtask Queue`に移行するのでこのような結果になりました。先にデモした二重`for`ループ一番の違いです。

外側の関数が普通の関数でも使える。結果も同じです。
```jsx
// normal function
function normalFn(arrayLikeObj) {
  console.log('test start');
  // synchronous => asynchronous
  Array.from(arrayLikeObj, (item) => awaitElement(item));
  console.log('test end');
}
console.log('start');
normalFn(arrayLikeObj);
console.log('end');
// start
// test start
// test end
// end
// a // item = [a, b] => await element: a
// c // item = [c, d] => await element: c
// b
// d
```

## ArrayLike Object with calling `[Symbol.asyncIterator]`
では、`[Symbol.asyncIterator]`プロパティだけ使ったらまた、どうなるでしょうか。
```jsx
// ArrayLike Object with calling `[Symbol.asyncIterator]`
let arrayLikeObj = {
  0: ['a', 'b'],
  1: ['c', 'd'],
  length: 2,
  async*[Symbol.asyncIterator]() {
    for (let i = 0; i < this.length; i++) {
      yield this[i]
    }
  }
};
async function useArrayLike(arrayLike) {
  console.log('test start');
  // asynchronous
  // arrayLike didn't have [Symbol.iterator] property
  // for...of => for await...of => TypeError: arrayLikeObj is not iterable
  for await (let item of arrayLike) {
    for (let element of item) {
      console.log(element);
    }
  }
  console.log('test end');
}
console.log('start');
useArrayLike(arrayLikeObj);
console.log('end');
// start
// test start
// end
// a
// b
// c
// d
// test end
```
`[Symbol.asyncIterator]`だけ設置したので、`arrayLikeObj`オブジェクトには普通のイテレータメソッドが使えません。`for await...of`で要素を走査することで、要素の性質によってイテレータメソッドが使えるかもしれませんが、（`[Symbol.asyncIterator]`を呼び出すため）`for await...of`を使わなければならないことで、先ほどの例みたいに`Array.from(arrayLikeObj, (item) => awaitElement(item))`配列ごとに並列処理をすることができません。（厳密に言うと`Array.from()`は`arrayLikeObj`を配列にしてから`map()`する。配列にする段階で`Array.prototype[Symbol.iterator]`を使えるようになって、結果として`[[a, b], [c, d]]`でした。）

`for await...of`でイテレーションを行い、`map()`で`awaitElement()`に要素を入れて走査すると、また別の結果が出ました。
```jsx
async function awaitElement(item) {
  console.log('check');
  for await (let element of item) {
    console.log(element);
  }
}
async function useArrayLike(arrayLike) {
  console.log('test start');
  // asynchronous
  for await (let item of arrayLike) {
    item.map((element) => awaitElement(element));
  }
  console.log('test end');
}
console.log('start');
useArrayLike(arrayLikeObj);
console.log('end');
// start
// test start
// end // for await (let item of arrayLike)
// check
// check
// a
// b
// check
// check
// test end // for await (let element of item)
// c
// d
```
毎回の`for await...of`では`map()`は`a, b`、そして`c, d`に`awaitElement()`を施す。
そして`await`にあってしまうと、一度外側にメインに残る処理を終わらせてから出力する。

最後に`[Symbol.asyncIterator]`と`for await...of`についてもうちょっとまとめたいと思います。正直にいうと、`[Symbol.asyncIterator]`がプロパティとして使う理由があまりないかもしれません。`[Symbol.asyncIterator]`や`[Symbol.iterator]`、非同期の実現は呼び出し側の`await`にあるので、プロパティとしてわざわざ`[Symbol.asyncIterator]`を設置する必要がないと感じています。

**`await`が、`for await...of`の並行処理（`concurrent`）と`map()`の非同期関数による並列処理（`parallel`）の動きがわかったのは一番の収穫だと思います。**

# AsyncGenerator
`AsyncGenerator`は`[Symbol.asyncIterator]`に組み込んだメソッドですが、もとといえば同期処理から非同期へ変形したジェネレータのことです。

https://es6.ruanyifeng.com/?search=asyncIterator&x=2&y=15#docs/async-iterator#%E5%BC%82%E6%AD%A5-Generator-%E5%87%BD%E6%95%B0

参考文章から取った例です。
```jsx
// synchronous generator
function* syncGenerator(iterable, fn) {
  // call iterable object's [Symbol.iterator] property
  const iterator = iterable[Symbol.iterator]();
  while (true) {
    const { value, done } = iterator.next();
    if (done) break;
    // if generator didn't finsh yet, ouput fn(value)
    yield fn(value);
  }
}

// asynchronous generator
async function* asyncGenerator(iterable, fn) {
  const iterator = iterable[Symbol.iterator]();
  while (true) {
    // await => asynchronous
    const { value, done } = await iterator.next();
    if (done) break;
    yield fn(value);
  }
}
```
`async function`の内部に`await`を使うことによって非同期を実現するのです。

`async function`か、`asyncGenerator`で`for await...of`を通して呼び出すことができる。
```jsx
// use async function to call asyncGenerator
(async function () {
  for await (let line of readLines(filePath)) {
    console.log(line);
  }
})();
// or another asyncGenerator to call asyncGenerator
async function* prefixLines(asyncItarable) {
  for await (let line of asyncItarable) {
    yield '> ' + line;
  }
}
```

## Syntax
### AsyncGenerator(async function* expression)
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AsyncGenerator


### AsyncGeneratorFunction((async function* () {}).constructor)
https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/AsyncGeneratorFunction

下のように`(async function* () {}).constructor`コンストラクタを使ってシンプルな非同期ジェネレータを作ることができる。
```jsx
// (async function* () { }).constructor
// num
let num = 10;
const asyncGeneratorFn = (async function* () { }).constructor;
let createAsyncGenerator = new asyncGeneratorFn(
  'a',
  'num',
  'yield a * num'
);
let asyncGen = createAsyncGenerator(5, num);
console.log('start');
asyncGen
  .next()
  .then((result) => console.log(result));
console.log('end');
start
end
{ value: 50, done: false }

// arr
let arr = [1, 2, 3];
const asyncGeneratorFn = (async function* () { }).constructor;
let createAsyncGenerator = new asyncGeneratorFn(
  'a',
  'arr',
  'yield arr.map((item) => item * a)'
);
let asyncGen = createAsyncGenerator(5, arr);
console.log('start');
asyncGen
  .next()
  .then((result) => console.log(result));
console.log('end');
// start
// end
// { value: [ 5, 10, 15 ], done: false }
```
`async function*`で`createAsyncGenerator(5, arr)`を書き直したら下のようになります。
```jsx
// async function*
async function* foo(a, arr) {
  yield await Promise.resolve(arr.map((item) => item * a));
}
foo(5, [1, 2, 3])
  .next()
  .then((result) => console.log(result));
// { value: [ 5, 10, 15 ], done: false }
```

### yield* 
同期ジェネレータの`yield*`と使い方が同じです。
```jsx
// yield in async generator vs. await in async generator
async function* test1() {
  yield 'a';
  yield 'b';
  return 2;
}
async function* test2() {
  let result = yield* test1();
  console.log(result);
  // let awaitAsyncGeneratorFn = await test1(); // meaningless
  // console.log(awaitAsyncGeneratorFn); // Object [AsyncGenerator] {}
}

for await (let item of test2()) {
  console.log(item);
}
// a
// b
// 2 // result
```