ES6 Generator

function* myGeneratorFn(num) { 
  console.info('you are in Generator function!');
  yield;
  num = num * 2;
  if (num > 10) return num;
  else yield num;
  console.info('you are geting out Generator function!');
}
image.png
image.png

Generator是一个遍历器对象生成函数,内部封装了多个状态的状态机。(我的理解是一个允许执行中断、可半路与外部交互的函数)

  1. 定义:function* functionName
  2. yield:执行到此处暂时中断
  3. next:从中断的位置继续执行,直到return或函数结束(结束了也能调);

next

  • next方法的返回
{ 
  value: obj,   // yield后面跟的表达式,没有或没有return返回undefined
  done: true   // 遍历是否结束,即执行到return或函数结束
}

done返回true后表名遍历结束,再调用next永远返回{ value: undefined, done: true }

  • next方法可传参
function* foo(x) {
  var y = 2 * (yield (x + 1)); <---(yield (x + 1)) 为 12----------
  var z = yield (y / 3);                                         ^
  return (x + y + z);                                            |
}                                                                |
                                                                 |
var a = foo(5);                                                  |
a.next() // Object{value:6, done:false}                          |
a.next() // Object{value:NaN, done:false}                        |
a.next() // Object{value:NaN, done:true}                         |
                                                                 |
var b = foo(5);                                                  |
b.next() // { value:6, done:false }                              |
b.next(12) // { value:8, done:false }  -------12----------------->
b.next(13) // { value:42, done:true }

第一次next执行到第一个yield(只执行 yield xxx 这一部分),以此类推
参数为上一个(注意:是上一个,上一个!)yield语句的返回值,如代码中参数12的走向

yield

  • 仅generator中使用;用于forEach之类的回调方法中会报错
  • 用于表达式中时要使用()
let x = 'I am ' + (yield somevalue);
let num = yield somevalue;  // 这样不用

for ... of

function *foo() {
  yield 1;
  yield 2;
  yield 3;
  yield 4;
  yield 5;
  return 6;
}

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

// 扩展运算符
[...foo()] // [1, 2, 3, 4, 5]

// Array.from 方法
Array.from(foo()) //  [1, 2, 3, 4, 5]

// 解构赋值
let [x, y, z, i, j] = foo();  // x=1 ... j=5  
  • 不需要使用next方法
  • 一旦 done==true,循环就会中止,且不包含return 的值
  • 处理的是同步操作

Generator.prototype.throw()

var gen = function* gen(){
  try {
    yield console.log('a');
    yield console.log('b');
  } catch (e) {
    console.log('内部捕获', e);
  }
  yield console.log('c');
  yield console.log('d');
  yield console.log('e');
}

var g = gen(); 
try {
  g.next()             // a
  g.throw('my error')  // 内部捕获 undefined, c 
  g.next()             // d
  g.throw('my error2') // 外部捕获 my error2
} catch (e) {
  console.log('外部捕获', e);
} 
  • throw('my error') 被方法内try块catch到,且块内剩下的语句不再执行('my error'其实应该用new Error('a'))
  • throw会附带执行一次next方法:打印出了c

这种函数体内捕获错误的机制,大大方便了对错误的处理。多个yield语句,可以只用一个try...catch代码块来捕获错误。如果使用回调函数的写法,想要捕获多个错误,就不得不为每个函数内部写一个错误处理语句,现在只在Generator函数内部写一次catch语句就可以了。

Generator.prototype.return()

function* gen() {
  yield 1;
  yield 2;
  yield 3;
}

var g = gen();
g.next()        // { value: 1, done: false }
g.return('foo') // { value: "foo", done: true }
g.next()        // { value: undefined, done: true }
  • g.return会中断generator执行,方法参数为返回对象value的值
  • 如果generator方法中有finally,return方法会推迟到finally代码块执行完再执行
function* gen() {
  try {
    yield 1;
    yield 2;
    yield 3;
  } finally {
    console.info('finally~')     
    yield 4;
  }
}

var g = gen();
g.next()        // { value: 1, done: false }
g.return('foo') // finally~, { value: 4, done: false}
g.next()        // { value: "foo", done: true}

yield* 语句

用来在一个 Generator 函数里面执行另一个 Generator 函数

function* inner() {
  yield 'hello!';
}
// -------直接调用--------
function* fn1() {
  yield 'open';
  inner();
  yield 'close';
}
var gen = fn1()
gen.next()  // { value: "open", done: false }
gen.next()  // { value: "close", done: true } 
gen.next()  // { value: undefined, done: true }

// -------使用yield  --------
function* fn2() {
  yield 'open';
  yield  inner();
  yield 'close';
}
var gen = fn2()
gen.next()  // { value: "open", done: false }
gen.next()  // 返回一个遍历器对象
gen.next()  // { value: "close", done: true }

// -------使用yield*  --------
function* fn3() {
  yield 'open';
  yield* inner();
  yield 'close';
}
var gen = fn3()
gen.next()  // { value: "open", done: false }
gen.next()  // { value: "hello", done: false }
gen.next()  // { value: "close", done: true }

如果yield*后面跟一个数组对象(如["a", "b", "c"]),就需要多调用几次(3次)next才能全拿到;如果是yield就会一次拿到一个数组。

Generator与一般function的不同

  1. Generator函数永远返回一个遍历器,它定义在this上的属性在外面访问不到
  2. 不能跟new命令一起出现,因为new的本质上是个constructor

让Generator函数返回一个正常的对象实例

Generator与状态机

Generator与协程

异步操作的同步化表达

你可能感兴趣的:(ES6 Generator)