# 深入浅出 Generator

## 概念基本

形式上，Generator 是一个函数，但具有两个区别于普通函数的特征：

* `function` 关键字和函数名之间有一个星号（\*）
* 函数体内部使用 `yield` 表达式，定义不同的内部状态（yield 代表产出）

内涵上可以将其理解为一个状态机 + 遍历器对象

```javascript
 function* helloWorldGenerator() {
    yield 'hello'
    yield 'world'
    return 'ending'
}

var hw = helloWorldGenerator()
```

如下调用：

```javascript
hw.next()
// { value: 'hello', done: false }

hw.next()
// { value: 'world', done: false }

hw.next()
// { value: 'ending', done: true }

hw.next()
// { value: undefined, done: true }
```

调用 Generator 函数，返回一个遍历器对象，代表 Generator 函数的内部指针。以后，每次调用遍历器对象的`next`方法，就会返回一个有着`value`和`done`两个属性的对象。`value`属性表示当前的内部状态的值，是`yield`表达式后面那个表达式的值；`done`属性是一个布尔值，表示是否遍历结束

## `yield` 表达式

由于 Generate 函数返回的遍历器对象，只有调用 next 方法才会遍历下一个内部状态，所以其实提供了一种可以暂停执行的函数。yield 表达式就是暂停标志。

遍历器对象的 next 方法的运行逻辑如下：

（1）遇到 yield 表达式，就暂停执行后面的操作，并**紧跟在 `yield` 后面的那个表达式的值，作为 `Generate` 返回的对象的 `value` 属性值**

（2）下一次调用 `next` 方法时，再继续往下执行，直到遇到下一个 `yield` 表达式

（3）如果没有再遇到新的 yield 表达式，就一直运行到函数结束，直到 `return` 语句为止，并将 `return` 语句后面的表达式的值，作为返回的对象的 `value` 属性值

（4）如果该函数没有 `return` 语句，则返回的对象的 `value` 属性值为 `undefined`

需要注意的是，`yield` 表达式后面的表达式，只有当调用 `next` 方法、内部指针指向该语句时才会执行，因此等于 `js` 提供了手动的“惰性求值“的语法功能。

例如：

```javascript
function* gen() {
    yield 123 + 456
}
```

`yield` 后面的表达式 `123 + 456`，不会立即求值，只会在 `next` 方法将指针移到这一句时，才会求值。

tips：yield 表达式只能用在 Generator 函数中

## next 方法的参数

`yield` 表达式**本身没有返回值**，或者说总是返回 `undefined`。`next` 方法可以带一个参数，该参数就会被当作一个 `yield` 表达式的返回值

```javascript
function*  f() {
    for(var i = 0; true; i++) {
        var reset = yield i
        if (reset) i = -1
    }
}

var g = f()

g.next() // { value: 0, done: false }
g.next() // { value: 1, done: }
g.next(true) // { value: 0, done: false } 
```

上面代码先定义了一个可以无限运行的 `Generate` 函数 `f`，**如果 `next` 方法没有参数，每次运行到 `yield` 表达式，变量 `reset` 的值总是 `undefined`**。当 `next` 方法带一个参数 `true` 时，变量 `reset` 就被重置为这个参数（即 `true`），因此 `i` 会等于 `-1`，下一轮循环就会从 `-1` 开始递增。

## 简单使用

`Generator` 函数中的 `yield` 语句执行可以通过 `next` 顺序执行，`yield` 本身没有返回值，当 `yield` 语句用 `next` 执行时会返回 `yield` 语句返回的值。可以通过 `next` 传入值给 `yield`，从而使它具有返回值
