ES6—Generator函数

简介

基本概念

  1. 是一种异步编程解决方案
  2. 可以看成一个状态机,封装了多个内部状态。
  3. 执行Generator函数会返回一个遍历器对象

特征

  1. function命令与函数名之间有一个星号
  2. 函数体内部使用yield语句定义不同的内部状态
    只有调用next方法且内部指针指向该语句时才会执行yield后面的表达式
  3. yield只能用在Generator函数中
var arr = [1, [[2, 3], 4], [5, 6]];

var flat = function* (a) {
// forEach的参数是普通函数,使用yield报错
  a.forEach(function (item) {
    if (typeof item !== 'number') {
      yield* flat(item);
    } else {
      yield item;
    }
  });
};

for (var f of flat(arr)){
  console.log(f);
}

与Iterator接口的关系

可以把Generator赋值给对象的Symbol.iterator属性,是对象具有Iterator接口,可以被...遍历

var mine = {};
mine[Symbol.iterator] = function* () {
  yield 1;
  yield 11;
  yield 111;
}

console.log([...mine]);  //[ 1, 11, 111 ]

next方法参数

如果没有参数,每次运行到yield语句时,变量reset的值总是underfined

var reset = yield i;

注意:next方法的参数表示上一条yield语句的返回值,所以第一次next方法的参数传递是无效的。(第一个next方法用来启动遍历器对象,所以不用带参数)

for…of循环

next方法返回对象的done属性为true,for…of循环就会终止,且不包含该返回对象

// return语句返回的done属性为true
function* foo() {
  yield 1;
  yield 2;
  yield 3;
  yield 4;
  yield 5;
  return 6;
}

for (let v of foo()) {
  console.log(v);
}
// 1 2 3 4 5

通过Generator函数为对象加上遍历接口

function* objectEntries(obj) {
  let propKeys = Reflect.ownKeys(obj);  // 获取该对象所有键
  for (const propKey of propKeys) {
    yield [propKey, obj[propKey]]
  }
}

let jane = { first: 'Jane', last: 'Doe'};
for (const [key, value] of objectEntries(jane)) {
  console.log(`${key}:${value}`);
}

或者将Generator函数加到对象的Symbol.iterator属性

function* objecyEntries2() {
  let propKeys = Object.keys(this);
  for (const propKey of propKeys) {
    yield [propKey, this[propKey]]
  }
}
let janee = { first: 'Jane', last: 'Doe'};
janee[Symbol.iterator] = objecyEntries2;
for (const [key, value] of janee) {
  console.log(`${key}:${value}`);
}

Generator.prototype.throw()

throw方法可以在函数体外抛出错误,然后在Generator函数体内捕获。

Generator.prototype.return()

返回给定的值,终结Generator函数的遍历。

yield*表达式

  1. 用来在一个Generator函数里面执行另一个Generator函数
/**
 *  yield*
 */
function* foo() {
  yield 'a';
  yield 'b';
}

function* bar() {
  yield 'x';
  yield* foo();
  yield 'y';
}

for (const v of bar()) {
  console.log(v);  // x a b y
}

  1. yield* 遍历完全二叉树

作为对象属性的Generator函数

/**
 * 作为对象属性的Generator函数
 */
let obj = {
  * myGeneratorMethod() {
    // your code
  }
};
// 等价于
let obj = {
  myGeneratorMethod: function* () {
    // your code 
  }
}

Generator函数 this

Generator函数返回一个遍历器(Generator函数的实例),也继承了Generator函数的prototype对象上方法。

/**
 * 遍历器obj是g的实例,继承了g.prototype
 */
function* g() {
  g.prototype.hello = function() {
    return 'hi';
  };

  let obj = g();
  obj instanceof g //true
  obj.hello()  // 'hi'
}

如果把g当作普通的构造函数就不会生效,因为g返回的总是遍历器对象,而不是this对象

/**
 * 如果把g当作普通的构造函数就不会生效,因为g返回的总是遍历器对象,而不是this对象
 */
function* g() {
  this.a = 11;
}

let obj = g();
obj.a  // undefined

获得this的方法:生成一个空对象,使用call方法绑定Generator函数内部的this,构造函数调用后空对象就是Generator函数的实例对象。

/**
 * 获取this的方法
 */
function* F() {
  this.a = 1;
  yield this.b = 12;
}
var obj = {};
var f = F.call(obj);
obj.a // 1
obj.b // 12

含义

Generator与状态机

Generator实现少了用来保存状态的外部变量,更简洁、安全。

/**
 * Generator实现状态机
 */

//状态机
var ticking = true;
var clock = function() {
  if(ticking)
    console.log('Tick');
  else
    console.log('Tock');
  ticking = !ticking;
}

// Generator实现
var clock = function* () {
  while (true) {
    console.log('Tick');
    yield;
    console.log('Tock');
    yield;
  }
};

Generator与协程

  1. 协程与子例程的差异
  2. 协程与普通线程的差异

应用

Generator可以暂停函数执行,返回任意表达式的值。

异步操作的同步化表达

调用next方法时往后执行,不需要写回调函数。

/**
 * 异步操作的同步化表达
 */
function* loadUI() {
  showLoadingScreen();
  yield loadUIDataAsynchronously();
  hideLoadingScreen();
}
var loader = loadUI();
loader.next();  // 加载UI
loader.next();  // 卸载UI

实现AJAX

/**
 * 通过Generator函数部署AJAX操作,可以用同步方式表达
 */
function* main() {
  var result = yield request("http://yoururl");
  var resp = JSON.parse(result);
  console.log(resp.value);
}

function request(url) {
  makeAjaxCall(url, function(response) {
    it.next(response);
  });
}

var it = main();
it.next();

控制流管理

/**
 * 控制流管理
 */
// 多部操作
step1(function (value1) {
  step2(value1, function (value2) {
    step3(value2, function (value3) {
      // do something...
    });
  });
});

// Promise
Promise.resolve(step1).then(step2).then(function (value3) {
  // do something
}, function(err) {
  // Handle any err
}).done();

// Generator(只适用同步操作)
function* longRunningTask(value1) {
  try{
    var value2 = yield step1(value1);
    var value3 = yield step1(value2);
    var value4 = yield step1(value3);
  } catch(e) {
    // Handle any err
  }
}

部署Iterator接口

/**
 * 利用Generator函数在任意对象上部署Iterator接口
 */
function* iterEntries(obj) {
  let keys = Object.keys(obj);
  for (let i = 0; i < keys.length; i++) {
    const key = keys[i];
    yield [key, obj[key]];
  }
}

let myObj = { foo: 3, bar: 7};
for (const [key, value] of iterEntries(myObj)) {
  console.log([key, value]);  // foo 3, bar 7
}

作为数据结构

Generator函数可以返回一系列的值,可以对任意表达式提供类似数组的接口


/**
 * 作为数据结构
 */
function* doStuff() {
  yield fstat.readFile.bind(null, 'hello.txt');
  yield fstat.readFile.bind(null, 'wrold.txt');
  yield fstat.readFile.bind(null, 'and-such.txt');
}

for (const task of doStuff()) {
  // 调用task函数
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值