JavaScriptのgeneratorについて part1

# 初めに
今回はジェネレータについてまとめていきたいと思います。

今回の参考文章はこちらです。
[Generators - javascript.info](https://javascript.info/generators)
[yield - MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/yield)
[yield* - MDN](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/yield*)

# Syntax
`function*`：generator関数の構文。
`yield`：generator関数内から返す値を定義する。
`next()`：generator関数から返却された値を取得する。結果は常にオブジェクト`{value: , done: }`。
```jsx
function* generateSequence() {
  yield 1
  yield 2
  return 3
}

let generator = generateSequence()

console.log(generator) // Object [Generator] {}

console.log(generator.next()) // { value: 1, done: false }
console.log(JSON.stringify(generator.next())) // {"value":2,"done":false}
console.log(generator.next()) // { value: 3, done: true }
console.log(generator.next()) // { value: undefined, done: true }
```
generator関数は普通の関数のように呼び出すことができません。（実質上はiterableオブジェクトに近いと思う。）
`next()`メソッドの呼び出しを通して一番近い`yield`文までコードを実行した後一時停止し値を取得する。次の`next()`が呼び出されるとまた再開する。

`next()`で最後の`yield`の値を取得、あるいは`return`に到達したら`done: true`になります。最終結果が処理された以上の呼び出しは意味ありません。

# iteration
`next()`ではなく`for..of`でループ処理で値だけを取り出すことができます。
```jsx
function* generateSequence() {
  yield 1
  yield 2
  yield 3
}

let generator = generateSequence()
for (let value of generator) {
  console.log(value)
}
console.log(generator.next())

// 1
// 2
// 3
// { value: undefined, done: true }
```

[　`for..of`　](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of#closing_iterators)でジェネレータを実行する場合は、`throw`、`return`に遭ったら完全に停止されます。
`break`なら外側のコードで最初の`yield`から実行します。
```jsx
function* breakGenerator() {
  yield 1
  yield 2
  yield 3
}

for (let value of breakGenerator()) {
  console.log(value)
  break
}

console.log('done')
console.log(breakGenerator().next())

// 1
// done
// { value: 1, done: false }
```

そしてスプレッド構文で展開したり、
```jsx
function* generateSequence() {
  yield 1
  yield 2
  yield 3
}

let sequence = [0, ...generateSequence()]
console.log(sequence) // [ 0, 1, 2, 3 ]
```

`for`ループで反復処理を行ったりすることができます。
```jsx
function* generateSequence(start, end) {
  for (let i = start; i <= end; i++) {
    yield i
  }
}

let sequence = [...generateSequence(1, 5)]
console.log(sequence) // [ 1, 2, 3, 4, 5 ]
```

# [Symbol.iterator] in object
[　`[Symbol.iterator]`　](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Symbol/iterator)でオブジェクトにデフォルトの`iterator`を設置できます。
```jsx
let range = {
  from: 1,
  to: 5,
  *[Symbol.iterator]() {
    for (let value = this.from; value <= this.to; value++) {
      yield value
    }
  }
}

console.log([...range]) // [ 1, 2, 3, 4, 5 ]
```
（`*[Symbol.iterator]() {}`は短縮記法。）
スプレッド構文ではなく、オブジェクトから`[Symbol.iterator]`の呼び出しで`next()`を利用することもできます。
```jsx
console.log(range[Symbol.iterator]) // [GeneratorFunction: [Symbol.iterator]]

let iterator = range[Symbol.iterator]()
console.log(iterator.next()) // { value: 1, done: false }
console.log(iterator.next()) // { value: 2, done: false }
```

# generator composition
`yield*`：外部から別のジェネレータ、あるいは反復可能なオブジェクトに実行を委任する。
```jsx
function* iterableObj() {
  yield* [1, 2]
  yield* '34'
  yield* Array.from(arguments)
}

let iterator = iterableObj(5, 6)

console.log(iterator.next()) // { value: 1, done: false }
console.log(iterator.next()) // { value: 2, done: false }
console.log(iterator.next()) // { value: '3', done: false }
console.log(iterator.next()) // { value: '4', done: false }
console.log(iterator.next()) // { value: 5, done: false }
console.log(iterator.next()) // { value: 6, done: false }
```
`yield*`で別のジェネレータへ、
```jsx
function* generateSequence(start, end) {
  for (let i = start; i <= end; i++) yield i
}

function* generatePasswordCodes() {
  // 0..9
  yield* generateSequence(48, 57)
  // A..Z
  yield* generateSequence(65, 90)
  // a..z
  yield* generateSequence(97, 122)
}

let str = ''
for (let code of generatePasswordCodes()) {
  str += String.fromCharCode(code)
}

console.log(str)
// 0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
```

# about `yield`

[“yield” is a two-way street](https://javascript.info/generators#yield-is-a-two-way-street)例の説明では、`yield`は外部へ値の返しや、外部の`next()`の引数を内部の値として入れることができます。`yield`の動きを見ていきたいと思います。
```jsx
function* twoWayStreet() {
  // yield can output or input value
  let result = yield '2 + 2?'

  console.log(result)
}

let generator = twoWayStreet()
console.log(generator.next().value) // 2 + 2? // output
// generator.next(4) // 4 // input

console.log(generator.next(4)) // { value: undefined, done: true }
```
`yield`は同時に内部⇔外部、値の転送の仲介を担当しているように見えます。

```jsx
function* twoWayStreet() {
  let result1 = yield '2 + 2?'
  console.log(result1)

  let result2 = yield '3 + 3?'
  console.log(result2)
}

let generator = twoWayStreet()

console.log(generator.next())
// { value: '2 + 2?', done: false }

console.log(generator.next(4))
// 4
// { value: '3 + 3?', done: false }

console.log(generator.next(9))
// 9
// { value: undefined, done: true }
```
最初コンソールを見て、なぜこんな結果がでるのかわかりませんでした。
`next()`呼ばれると次の`yield`へ移動し値を取るのに、なぜ二番目の`next`が一番目の`yield`に値を入れたのか？

全体の動きとしては、
`console.log(generator.next())` // { value: '2 + 2?', done: false }
↓　（一時停止）
`generator.next(4)` let result1 = 4
　`console.log(result1)` // 4
　`generator.next()` // { value: '3 + 3?', done: false }
↓
`generator.next(9)` let result2 = 9
　`console.log(result2)` // 9
　`generator.next()` // { value: undefined, done: true }

ほかのコードでテストしてみると`yield`は一定のルールで値を転送することに気づき、上のように整理してみました。
どうやら`next()`の呼び出しは順次に`yield`から値を取得するが、`next()`に引数を入れた場合は外部から内部、そして内部から外部へ、という順序で値を渡す。

実は上のコード少し変更してみると、
```jsx
console.log(generator.next(4))
// { value: '2 + 2?', done: false }

console.log(generator.next(9))
// 9
// { value: '3 + 3?', done: false }

console.log(generator.next(5))
// 5
// { value: undefined, done: true }
```
`console.log(generator.next(4))` // まだ一番目の`yield`の値実行していないので値の転送先がわかりません。
`console.log(generator.next())` // { value: '2 + 2?', done: false }
↓　（一時停止）
`generator.next(9)` let result1 = 9
　`console.log(result1)` // 9
　`generator.next()` // { value: '3 + 3?', done: false }
↓
`generator.next(5)` let result2 = 5
　`console.log(result2)` // 5
　`generator.next()` // { value: undefined, done: true }

# generator.throw
```jsx
function* generateThrow() {
  try {
    let result = yield '2 + 2?'
    console.log('The execution does not reach here, because the exception is thrown above')
  } catch (err) {
    console.log(err)
  }
}

let generator = generateThrow()
// console.log(generator) // Object [Generator] {}
generator.throw(new Error('The answer is not found in my database'))
// Error: The answer is not found in my database
```
`next()`のほか、`throw()`メソッドでエラーを投げることもできます。
`next()`のように`throw`も一番近い`yield`文へ値を転送、そして`try...catch`文でエラーを発見したとたんにすぐ`catch(err)`へ移動しました。

下の例はスロー無しで試してみました。
```jsx
function* generateThrow() {
  try {
    let result = yield error()
    console.log('The execution does not reach here, because the exception is thrown above')
  } catch (err) {
    console.log(err)
  }
}

let generator = generateThrow()

function error() {
  throw new Error('Generate Error!')
}

generator.next()
// Error: Generate Error!
```

# generator.return
```jsx
function* generatorReturn() {
  yield 1
  yield 2
  yield 3
}

let generator = generatorReturn()

console.log(generator.next()) // { value: 1, done: false }
console.log(generator.return('stop')) // { value: 'stop', done: true }
console.log(generator.next()) // { value: undefined, done: true }
```
ここ`return()`の動きは`next()`とちょっと違うと思いますが、下の順序少し変更してみたら、

```jsx
console.log(generator.return('stop')) // { value: 'stop', done: true }
console.log(generator.next()) // { value: undefined, done: true }
console.log(generator.next()) // { value: undefined, done: true }
```
`next(4)`は外部⇒内部、内部⇒外部、といった既定のルートによって値を転送するけど、`return()`は値を`value`へ渡し、強制的にジェネレータを閉じる（`done: true`）ように見えます。

# Pseudo-random generator
```jsx
function* pseudoRandom(seed) {
  let value = seed

  while (true) {
    value = value * 16807 % 2147483647
    yield value
  }
}

let generator = pseudoRandom(1)

console.log(generator.next().value) // 16807
console.log(generator.next().value) // 282475249
console.log(generator.next().value) // 1622650073
```
ごく簡単の例ですが、ジェネレータに`while(true){}`を利用するととても便利だと感じています。一定の範囲内でコードを生成してほしいならジェネレータ関数に引数、あるいは`yield`で工夫していい。範囲のないコード生成は`while(true){}`を使えばいい、途中で止めてほしいなら`return()`。