# 生成器（Generator）——《实战 ES2015》章节试读

## 来龙

``const fibonacci = [ 0, 1 ] const n = 10  for (let i = 2; i < n - 1; ++i) {   fibonacci.push(fibonacci[i - 1] + fibonacci[i - 2]) } console.log(fibonacci) //=> [0, 1, 1, 2, 3, 5, 8, 13, 21] ``

``function* fibo() {   let a = 0   let b = 1    yield a   yield b    while (true) {     let next = a + b     a = b     b = next     yield next   } }  let generator = fibo()  for (var i = 0; i < 10; i++)   console.log(generator.next().value) //=> 0 1 1 2 3 5 8 13 21 34 55 ``

## 基本概念

### 生成器函数（Generator Function）

``function* fibo() {   // ... } ``

``const fnName = function*() { /* ... */ } ``

### 生成器（Generator）

``const inputValue = yield outputValue ``

## 使用方法

### 构建生成器函数

``function* genFn() {   let a = 2    yield a    while (true) {     yield a = a / (2 * a + 1)   } } ``

### 启动生成器

``const gen = genFn() ``

``class Generator {   next(value)   throw(error)   [@iterator]() } ``

``{   value: Any,   done: Boolean } ``

### 运行生成器内容

``for (const a of gen) {   if (a < 1/100) break    console.log(a) } //=> //  2 //  0.4 //  0.2222222222 //  ... ``

## 深入理解

### 生成器函数以及生成器对象的检测

``function Point(x, y) {   if (!(this instanceof Point)) return new Point(x, y)   // ... }  const p1 = new Point(1, 2) const p2 = Point(2, 3) ``

``String()  //=> "" Number()  //=> 0 Boolean() //=> false Object()  //=> Object {} Array()   //=> [] Date()    //=> the current time RegExp()  //=> /(?:)/ ``

TIPS: 在代码风格检查工具 ESLint 中有一个可选特性名为 no-new 即相比使用 new，更倾向于使用直接调用构造函数来创建实例。

``function* genFn() {} const gen = genFn()  console.log(genFn.constructor.prototype) //=> GeneratorFunction console.log(gen.constructor.prototype)   //=> Generator ``

``console.log(gen instanceof genFn) //=> true ``

``function isGeneratorFunction(fn) {   const genFn = (function*(){}).constructor    return fn instanceof genFn }  function* genFn() {   let a = 2    yield a    while (true) {     yield a = a / (2 * a + 1)   } }  console.log(isGeneratorFunction(genFn)) //=> true ``

``function isGeneratorFunction(fn) {   const genFn = (function*(){}).constructor    return (isGeneratorFunction = fn => fn instanceof genFn)(fn) } ``

``function isGenerator(obj) {   return obj.toString ? obj.toString() === '[object Generator]' : false }  function* genFn() {} const gen = genFn()  console.log(isGenerator(gen)) //=> true console.log(isGenerator({}))  //=> false ``

``function isGenerator(obj) {   return obj[Symbol && Symbol.toStringTag ? Symbol.toStringTag : false] === 'Generator' }  console.log(isGenerator(gen)) //=> true console.log(isGenerator({}))  //=> false ``

``function* genFn() {}  console.log(genFn[Symbol.toStringTag]) //=> GeneratorFunction ``

``function isGeneratorFunction(fn) {   return fn[Symbol && Symbol.toStringTag ? Symbol.toStringTag : false] === 'GeneratorFunction' }  console.log(isGeneratorFunction(genFn)) //=> true ``

``function isGeneratorFunction(fn) {   // If the current engine supports Symbol and @@toStringTag   if (Symbol && Symbol.toStringTag) {     return (isGeneratorFunction = fn => fn[Symbol.toStringTag] === 'GeneratorFunction')(fn)   }    // Using instanceof statement for detecting   const genFn = (function*(){}).constructor    return (isGeneratorFunction = fn => fn instanceof genFn)(fn) }  console.log(isGeneratorFunction(genFn)) //=> true ``

### 生成器嵌套

``// Newton-Cotes formulas function* newton_cotes(f, a, b, n) {   const gaps = (b - a) / n   const h = gaps / 2    for (var i = 0; i < n; i++) {     yield h / 45 *       (7 * f(a + i * gaps) +       32 * f(a + i * gaps + 0.25 * gaps) +       12 * f(a + i * gaps + 0.5 * gaps) +       32 * f(a + i * gaps + 0.75 * gaps) +       7 * f(a + (i + 1) * gaps))   } } ``

``function* foo() {   yield 1   yield 2 }  function* bar() {   yield* foo()   yield 3   yield 4 }  for (const n of bar()) console.log(n) //=> //  1 //  2 //  3 //  4 ``

``function* Part1(n) {   yield* newton_cotes(x => Math.pow(x, 2), -2, 0, n) }  function* Part2(n) {   yield* newton_cotes(x => Math.sin(x), 0, 2, n) }  function* sum() {   const n = 100    yield* Part1(n)   yield* Part2(n) } ``

### 生成器 ≈ 协程？

``io1((err, res1) => {   io2(res1, (err, res2) => {     io3(res2, (err, res3) => {       io4(res3, (err, res4) => {         io5(res5, (err, res5) => {           // ......         })       })     })   }) }) ``

``// Before function echo(content, callback) {   callback(null, content) }  // After function echo(content) {   return callback => {     callback(null, content)   } } ``

``function run(genFn) {   const gen = genFn()    const next = value => {     const ret = gen.next(value)     if (ret.done) return      ret.value((err, val) => {       if (err) return console.error(err)        // Looop       next(val)     })   }    // First call   next() } ``

``run(function*() {   const msg1 = yield echo('Hello')   const msg2 = yield echo(`\${msg1} World`)    console.log(msg2) //=> Hello Wolrd }) ``

``import co from 'co' import { promisify } from 'bluebird' import fs from 'fs' import path from 'path'  const filepath = path.resolve(process.cwd(), './data.txt') const defaultData = new Buffer('Hello World')  co(function*() {   const exists = yield promisify(fs.exists(filepath))    if (exists) {     const data = yield promisify(fs.readFile(filepath))     // ...   } else {     yield promisify(fs.writeFile(filepath, defaultData))     // ...   } }) ``