# 1.Generator 函数

  1. 特征
  • function 关键字与函数名之间有一个星号;
  • 函数体内部使用 yield 表达式,定义不同的内部状态;
  1. 执行 执行 Generator 函数会返回一个遍历器对象,也就是说,Generator 函数除了状态机,还是一个遍历器对象生成函数。返回的遍历器对象,可以依次遍历 Generator 函数内部的每一个状态。

  2. yield 与 return 区别

  • 相似之处:都能返回紧跟在语句后面的那个表达式的值
  • 每次遇到 yield,函数暂停执行,下一次再从该位置继续向后执行,而 return 语句不具备位置记忆的功能
  • 一个函数里面,只能执行一次(或者说一个)return 语句,但是可以执行多次(或者说多个)yield 表达式
  • 正常函数只能返回一个值,因为只能执行一次 return;Generator 函数可以返回一系列的值,因为可以有任意多个 yield
  1. 案例解析
function* foo(x) {
  var y = 2 * (yield x + 1);
  var z = yield y / 3;
  return x + y + z;
}

var a = foo(5); //这一步只是执行返回了迭代指针
a.next(); // Object{value:6, done:false} 执行到(yield x + 1)就停止了
a.next(); // Object{value:NaN, done:false} 执行到yield y / 3,因为next没有传参数,所以表示(yield x + 1)这个表达式的值是undefined,导致y是NaN
a.next(); // Object{value:NaN, done:true} 执行return x + y + z,z的值是yield y / 3的返回值,因为next没有传参,导致返回值是undefined

var b = foo(5);
b.next(); // { value:6, done:false } 执行到(yield x + 1)就停止了
b.next(12); // { value:8, done:false } 执行到yield y / 3,因为next传参数12,所以表示(yield x + 1)这个表达式的值是12,导致y是24,所以yield y / 3是8
b.next(13); // { value:42, done:true } 执行return x + y + z,z的值是yield y / 3的返回值,因为next传参13,导致z= yield y / 3=13  x+y+z=5+24+13=42

# 1.Generator 函数的异步应用

  1. 异步编程的方法
  • 回调函数
  • 事件监听
  • 发布/订阅
  • Promise 对象
  1. 回调函数 所谓回调函数,就是把任务的第二段单独写在一个函数里面,等到重新执行这个任务的时候,就直接调用这个函数
fs.readFile('/etc/passwd', 'utf-8', function(err, data) {
  if (err) throw err;
  console.log(data);
});
  1. promise Promise 的写法只是回调函数的改进,使用 then 方法以后,异步任务的两段执行看得更清楚了,Promise 的最大问题是代码冗余,原来的任务被 Promise 包装了一下,不管什么操作,一眼看去都是一堆 then,原来的语义变得很不清楚。
var readFile = require('fs-readfile-promise');

readFile(fileA)
  .then(function(data) {
    console.log(data.toString());
  })
  .then(function() {
    return readFile(fileB);
  })
  .then(function(data) {
    console.log(data.toString());
  })
  .catch(function(err) {
    console.log(err);
  });
  1. Generator 函数 {value:'',done:true}value 属性是 yield 语句后面表达式的值,表示当前阶段的值;done 属性是一个布尔值,表示 Generator 函数是否执行完毕,即是否还有下一个阶段.
var g = gen();
var result = g.next();

result.value
  .then(function(data) {
    return data.json();
  })
  .then(function(data) {
    g.next(data);
  });
  1. Thunk 函数 编译器的“传名调用”实现,往往是将参数放到一个临时函数之中,再将这个临时函数传入函数体。这个临时函数就叫做 Thunk 函数。
// ES6版本
const Thunk = function(fn) {
  return function(...args) {
    return function(callback) {
      return fn.call(this, ...args, callback);
      return 'data';
    };
  };
};
function f(a, cb) {
  cb(a);
}
const ft = Thunk(f);

ft(1)(console.log); // 1
  1. Thunk 函数的自动流程管理 Thunk 函数真正的威力,在于可以自动执行 Generator 函数。下面就是一个基于 Thunk 函数的 Generator 执行器。
function run(fn) {
  var gen = fn();

  function next(err, data) {
    var result = gen.next(data);
    if (result.done) return;
    result.value(next);
  }

  next();
}

function* g() {
  // ...
}

run(g);