ES6基础:Generator生成器

Posted 余光、

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ES6基础:Generator生成器相关的知识,希望对你有一定的参考价值。

Generator 生成器

Generator是一种奇特的函数形式,因为它的语法行为与传统函数完全不同,在平时项目中的使用频率也很低,大家经常将它忽略,甚至没有了解过,毕竟不熟悉它对日常开发也影响不大。

本文不会去深究它的实现原理,而是希望让大家了解到它被创造出来是为了解决什么问题,我们用它可以做什么?

一、了解生成器

生成器可以在执行当中暂停自身,可以立即恢复执行也可以过一段时间之后恢复执行。所以显然它并不像普通函数那样保证运行到完毕。

1.1 特征

生成器是一个状态管理的机器,内部可以保存若干个状态,且状态与状态之间需要手动触发。

简单的理解:它就是一种特别的函数,它包含两个特征:

  • function关键字与函数名之间有一个星号;
  • 函数体内部使用 yield 表达式,定义不同的内部状态(yield 在英语里的意思就是“产出”)。

1.2 运行第一个生成器

function* generatorOne() {
  yield 1;
  return 2;
}

const g = generatorOne(); // Generator {<suspended>}
g.next(); // {value: 1, done: false}
g.next(); // {value: 2, done: true}

注意:

  1. function *foo() {...} , function* foo() {...} , function * foo() {...} 都是生成器声明
  2. 尽管生成器用 * 声明,但执行起来还和普通函数一样:

二、yield 关键字

生成器内有一个关键字 yield,它是状态切换停止的标志。

2.1 运行逻辑

  • 每次执行next()
    1. 遇到yield 表达式,暂停执行后面的操作,yield紧跟着的表达式的值,作为返回的对象的value属性值;
    2. 如果没有遇到yield 表达式,就一直运行直到 return 语句为止,并将return语句后面的表达式的值,作为返回的对象的value属性值;
    3. 如果该函数没有return语句,则返回的对象的 value 属性值为 undefined。

需要注意的是,yield 表达式,只有当调用 next 方法、内部指针指向该语句时才会执行,因此等于为 javascript 提供了手动的“惰性求值”(Lazy Evaluation)的语法功能。

2.2 实际使用

结合上一小节的文字描述,我们来逐步分析一下

function* generatorSleep(a, b) {
  console.log(1);
  console.log(2);
  yield;
  console.log(a + b);
  return "结束";
}
const i = generatorSleep(100, 200); // 创建生成器

// 消费1次,暂停在第一个yield表达式,并且将表达式的值返回值对象的value属性中
i.next();
// 输出:1、2
// 返回:{ value: undefined, done: false }

// 消费1次,没有新的yield,遇到return,并且将表达式的值返回值对象的value属性中
i.next();
// 返回:{ value: '结束', done: false }

看了上面最后一行结果的同学可能会存在疑问,为什么第一次valueundefiend

因为:紧跟在yield后面没有表达式,也没有返回值,value自然是undefined

一个无限迭代器

function* foo() {
  let index = 1;
  while (true) {
    yield index++;
  }
}

const i = foo();
console.log(i.next().value); // 1
console.log(i.next().value); // 2
console.log(i.next().value); // 3
console.log(i.next().value); // 4

上面是一个生成器中的 while…true 循环,每次迭代都会 yield 表达式都会得到index这个结果。它表示执行的次数。

三、方法

3.1 next()

调用next()是改变状态的唯一方式

返回一个 yield 表达式生成的值。它是一个包含属性 donevalue 的对象。该方法也可以通过接受一个参数用以向生成器传值。

下面就是它最常见的使用方式:

function* generator() {
  const a = yield 1;
  const b = yield 2;
  return "结束了";
}
const i = generator(); // Generator
i.next(); // {value: 1, done: false}
i.next(); // {value: 2, done: false}
// 此时done 转变为true
i.next(); // {value: '结束', done: true}

next方法可以有参数

许多同学都会关心类似下面的代码,var x = yield 10;,x会是什么?

function* foo() {
  var x = yield 10;
  console.log(x);
}

const i = foo();
i.next(); // {value: 10, done: false}
i.next();
// undefined
// {value: undefined, done: true}

x的值是undefined,这表示yield 10本身没有返回值。为了给它指定返回值,我们可以这么做

function* gen() {
  while (true) {
    var value = yield null;
    console.log(value);
  }
}

var g = gen();
g.next(1);
// "{ value: null, done: false }"
g.next(2);
// 2
// "{ value: null, done: false }"

3.2 方法 return()

以下例子展示了一个简单的生成器和 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 }

注意:

  • 如果对已经处于“完成”状态的生成器调用return(value),则生成器将保持在“完成”状态。
  • 如果没有提供参数,则返回对象的 value 属性与示例最后的.next()方法相同。
  • 如果提供了参数,则参数将被设置为返回对象的 value 属性的值。

例如:

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

var g = gen();
g.next(); // { value: 1, done: false }
g.next(); // { value: 2, done: false }
g.next(); // { value: undefined, done: true }
g.return(); // { value: undefined, done: true }
g.return(1); // { value: 1, done: true }

3.3 throw()

throw() 方法用来向生成器抛出异常,并恢复生成器的执行,返回带有 done 及 value 两个属性的对象。

function* gen() {
  while (true) {
    try {
      yield 42;
    } catch (e) {
      console.log(e);
    }
  }
}

var g = gen();
g.next(); // { value: 42, done: false }
g.throw(new Error("Something went wrong")); // Error: Something went wrong

写在最后

  • 花名:余光
  • WX:j565017805
  • 邮箱:webbj97@163.com

其他沉淀

以上是关于ES6基础:Generator生成器的主要内容,如果未能解决你的问题,请参考以下文章

ES6生成器函数generator

JavaScript中ES6新特性-Generator异步方案

学习ES6生成器(Generator)

Generator

ES6中generator(生成器)函数的应用

ES6 - Generator生成器