Generator语法

1.Generator 函数

Generator函数是ES6提供的一种异步解决方案,语法行为与传统函数完全不同。Generator函数是一个状态机,封装了多个内部状态。同时执行Generator函数会返回一个遍历器。调用遍历器的next方法,可以依次遍历Generator函数内部的每一个状态。

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

var hw = helloWorldGenerator();

形式上,Generator函数是一个普通函数,但有两个特征:1.function后面跟一个*号。2.函数中用yield来定义不同的状态

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

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

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

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

2.yield表达式

yield表达式就是Generator函数的暂停标志,遍历器的next方法的运行逻辑如下
(1)遇到yield表达式,就暂停执行后面的操作,并将yield后面的那个表达式的值作为返回对象的value属性的值
(2)下一次调用next方法时,再继续往下执行,直到遇到下一个yield表达式
(3)如果没有再遇到yield表达式,就一直运行到函数结束,直到return语句为止,并将return语句后面的表达式的值,作为返回对象的value属性的值
(4)如果该函数没有return语句,则返回的对象的value属性值为undefined。

Generator函数和Iterator接口的关系

任意一个对象的Symbol.Iterator方法,等于该对象的遍历器生成函数,调用该函数会返回该对象的一个遍历器对象。由于Generator函数返回的就是一个遍历器对象,所有我们可以把Generator函数直接赋值给Symbol.Iterator属性,从而使得对象具有Iterator接口。

var myIterable = {};
myIterable[Symbol.iterator] = function* () {
  yield 1;
  yield 2;
  yield 3;
};

[...myIterable] // [1, 2, 3]

3.Generator函数的next()、throw()、return()

这三个方法本质上是同一件事情,可以放在一起理解,就是让Generator函数恢复执行,并且使用不同的语句替换yield表达式。

  • next()是将yield表达式替换成一个值
const g = function* (x,y) {
  let result = yield x + y
  return result;
}

const gen = g(1,2)
gen.next()    //Object {value: 3, done: false}

gen.next(1); // Object {value: 1, done: true}
// 相当于将 let result = yield x + y
// 替换成 let result = 1;
  • throw()是将yield表达式替换成一个throw语句
gen.throw( new Error('出错了')); //Uncaught Error: 出错了
// 相当于将 let result = yield x + y
// 替换成 let result = throw(new Error('出错了'));
  • return()是将yield表达式替换成一个return语句
gen.return(2); // Object {value: 2, done: true}
// 相当于将 let result = yield x + y
// 替换成 let result = return 2;

4. yield*表达式

如果在Generator函数内部,调用另一个Generator函数。需要在前者的函数体内部,自己手动完成遍历

function* foo() {
  yield 'a';
  yield 'b';
}

function* bar() {
  yield 'x'
  //手动遍历 foo()
  for(let i of foo()) {
    console.log(i);
  }
  yield 'y'
}

for (let v of bar()) {
  console.log(v)
}

//x
//a
//b
//y

上面代码中,foo和bar都是Generator函数,在bar里面调用foo,就需要手动遍历foo,如果有多个Generator函数镶套就非常麻烦,为此说要有yield*的表达式

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

//等同于
function* bar() {
  yield 'x'
  yield 'a'
  yield 'b'
  yield 'y'
}

//等同于
function* bar() {
  yield 'x'
  for(let v of foo()){
    yield 'v'
  }
  yield 'y'
}

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

再来看一个对比的例子

function* inner() {
  yield 'hello!';
}

function* outer1() {
  yield 'open';
  yield inner();
  yield 'close';
}

var gen = outer1()
gen.next().value // "open"
gen.next().value // 返回一个遍历器对象
gen.next().value // "close"

function* outer2() {
  yield 'open'
  yield* inner()
  yield 'close'
}

var gen = outer2()
gen.next().value // "open"
gen.next().value // "hello!"
gen.next().value // "close"

上面例子中,outer2使用了yield,outer1没使用。结果就是,outer1返回一个遍历器对象,outer2返回该遍历器对象的内部值。
从语法角度看,如果yield表达式后面跟的是一个遍历器对象,需要在yield表达式后面加上星号,表明它返回的是一个遍历器对象。这被称为yield
表达式。

Generator函数的应用

1.异步操作的同步化表达

你可能感兴趣的:(Generator语法)