美文网首页JSJs
40.生成器和生成器函数

40.生成器和生成器函数

作者: 静昕妈妈芦培培 | 来源:发表于2021-11-24 13:45 被阅读0次

生成器是ES6中新增的一种函数控制、使用方案,它可以让我们更加灵活的控制函数什么时候暂停执行、继续执行等
平时我们编写的普通函数,这些函数终止的条件通常是返回值或者发生了异常,但是一旦终止,无法继续执行

普通函数执行结果

function foo() {
  console.log("函数开始执行");
  const value1 = 100;
  console.log(value1);
  // 位置1

  const value2 = 200;
  console.log(value2);
  // 位置2

  const value3 = 300;
  console.log(value3);
  // 位置3

  console.log("函数执行终止");
}

foo();

下面是执行结果:
函数开始执行
100
200
300
函数执行终止

如果我们希望函数执行到位置1暂停,即仅执行第一行代码
然后继续执行,到位置2暂停,
然后继续执行,到位置3暂停
然后继续执行到结束
可以使用生成器控制函数的执行

什么是生成器函数

生成器函数也是一个函数,但是和普通函数有一些区别:

  • 生成器函数需要在function的后面加一个符号*
  • 生成器函数可以通过yield关键字来控制函数的执行过程
  • 生成器函数执行会返回一个生成器(Generator)
    • 生成器事实上是一种特殊的迭代器

例:创建一个生成器函数

function * foo() {
  console.log("函数开始执行");
  const value1 = 100;
  console.log(value1);
  yield; // 位置1

  const value2 = 200;
  console.log(value2);
  yield; // 位置2

  const value3 = 300;
  console.log(value3);
  yield; // 位置3

  console.log("函数执行终止");
}

//执行生成器函数获取生成器
const generator = foo()
// 通过调用生成器的next方法去控制函数执行
generator.next()
console.log('------')
generator.next()
generator.next()
console.log('------')
generator.next()

执行结果如下:

函数开始执行
100
------
200
300
------
函数执行终止

生成器函数的执行流程

生成器函数调用会返回生成器,通过调用生成器的next方法,去控制生成器函数的执行
生成器的next方法执行会返回一个对象obj,此对象有done和value属性

生成器函数执行:

  • 当遇到yeild就暂停执行, 此时obj.done为false,obj.value为yield后跟的值
  • 当遇到return就终止执行, 此时obj.done为true,obj.value为return返回值
  • 当函数中代码执行完也会终止执行, 此时obj.done为true,obj.value为return返回值

例1:

//创建一个生成器函数
function * foo() {
  console.log("函数开始执行");
  const value1 = 100;
  console.log(value1);
  yield; // 位置1

  const value2 = 200;
  console.log(value2);
  yield; // 位置2

  const value3 = 300;
  console.log(value3);
  yield; // 位置3

  console.log("函数执行终止");
}

//执行生成器函数获取生成器
const generator = foo()
// 通过调用生成器的next方法去控制函数执行
console.log("执行结果1:", generator.next());
console.log("执行结果2:", generator.next());
console.log("执行结果3:", generator.next());
console.log("执行结果4:", generator.next());

执行结果:

函数开始执行
100
执行结果1: { value: undefined, done: false }
200
执行结果2: { value: undefined, done: false }
300
执行结果3: { value: undefined, done: false }
函数执行终止
执行结果4: { value: undefined, done: true }

例2:生成器函数中遇到return

//创建一个生成器函数
function * foo() {
  console.log("函数开始执行");
  const value1 = 100;
  console.log(value1);
  yield; // 位置1
  return;

  const value2 = 200;
  console.log(value2);
  yield; // 位置2

  const value3 = 300;
  console.log(value3);
  yield; // 位置3

  console.log("函数执行终止");
}

//执行生成器函数获取生成器
const generator = foo()
// 通过调用生成器的next方法去控制函数执行
console.log("执行结果1:", generator.next());
console.log("执行结果2:", generator.next());
console.log("执行结果3:", generator.next());
console.log("执行结果4:", generator.next());

执行结果:

函数开始执行
100
执行结果1: { value: undefined, done: false }
执行结果2: { value: undefined, done: true }
执行结果3: { value: undefined, done: true }
执行结果4: { value: undefined, done: true }

例3:return和yield后跟值

function* foo() {
  console.log("函数开始执行");
  const value1 = 100;
  console.log(value1);
  yield 800; // 位置1
  

  const value2 = 200;
  console.log(value2);
  yield; // 位置2
  return 700;

  const value3 = 300;
  console.log(value3);
  yield; // 位置3

  console.log("函数执行终止");
}

//执行生成器函数获取生成器
const generator = foo();
// 通过调用生成器的next方法去控制函数执行
console.log("执行结果1:", generator.next());
console.log("执行结果2:", generator.next());
console.log("执行结果3:", generator.next());
console.log("执行结果4:", generator.next());

执行结果:

函数开始执行
100
执行结果1: { value: 800, done: false }
200
执行结果2: { value: undefined, done: false }
执行结果3: { value: 700, done: true }
执行结果4: { value: undefined, done: true }

给生成器函数传参

  • 给生成器函数的第一段代码传参,通过在调用生成器函数时,作为实参传入
  • 给生成器函数的第二段及以上的代码传参,通过在调用生成器的next方法时给next传参,通过在上一段代码中的yeild表达式返回值接收

//创建一个生成器函数
function* foo(n1) {
  const value1 = 100 * n1;
  console.log(value1);
  const n2 = yield value1; // 位置1 
  //第二段代码中使用的参数,通过在上一段代码中的yeild表达式的返回值接收

  const value2 = 200 * n2;
  console.log(value2);
  const n3 = yield value2; // 位置2

  const value3 = 300 * n3;
  console.log(value3);
  const n4 = yield value3; // 位置3
  console.log("函数终止运行");
  return n4
}

//执行生成器函数获取生成器
// 给生成器函数的第一段代码传参,通过在调用生成器函数时,作为实参传入
const generator = foo(1);
// 通过调用生成器的next方法去控制函数执行
console.log("执行结果1:", generator.next());
//第二段代码中使用的参数,通过在第二次调用生成器的next方法时作为参数传入
console.log("执行结果2:", generator.next(2)); 
console.log("执行结果3:", generator.next(3));
console.log("执行结果4:", generator.next('abc'));


执行结果:

100
执行结果1: { value: 100, done: false }
400
执行结果2: { value: 400, done: false }
900
执行结果3: { value: 900, done: false }
函数终止运行
执行结果4: { value: 'abc', done: true }

生成器的return函数

  • 生成器使用return函数,来中止生成器函数的执行
  • return函数执行返回一个obj对象,有done和value属性,done的值为true
  • return函数执行时传入的参数,会作为obj.value的值
  • 使用生成器调用return函数相当于在生成器函数执行到的当前位置,添加一个return,return返回的值为return函数执行时传入的参数
//创建一个生成器函数
function* foo(n1) {
  const value1 = n1;
  console.log(value1);
  const n2 = yield value1; // 位置1 

  const value2 = n2;
  console.log(value2);
  const n3 = yield value2; // 位置2

  const value3 = n3;
  console.log(value3);
  const n4 = yield value3; // 位置3
  console.log("函数终止运行");
  return n4
}

//执行生成器函数获取生成器
// 给生成器函数的第一段代码传参,通过在调用生成器函数时,作为实参传入
const generator = foo(1);
// 通过调用生成器的next方法去控制函数执行
console.log(generator.next());
console.log('return函数执行结果', generator.return('return'))
console.log(generator.next(2));
console.log(generator.next(3));

执行结果:

1
{ value: 1, done: false }
return函数执行结果 { value: 'return', done: true }
{ value: undefined, done: true }
{ value: undefined, done: true }

生成器的throw方法:抛出异常

  • 生成器使用throw函数,来抛出异常
  • throw函数执行没有返回值
  • 使用生成器调用throw函数相当于在生成器函数执行到的当前位置,添加一个throw,抛出异常的错误为throw函数执行时传入的参数
  • 如果使用try/catch把上一个yield那行代码包裹起来,使用throw抛出异常,会调用catch的回调参数,然后继续执行下一段代码
  • 只要在代码生成器函数中有捕获执行throw抛出的异常,生成器可以通过调用next方法,继续执行下面的代码
  • 如果没有在代码生成器函数中没有捕获执行throw抛出的异常,则中止生成器函数


//创建一个生成器函数
function* foo(n1) {
  const value1 = n1;
  console.log(value1);
  try {
    yield value1; // 位置1
  } catch (error) {
    console.log("捕获到异常", error);
  }
  console.log("第二段代码开始执行");
  const value2 = 2;
  console.log(value2);
  yield value2; // 位置2

  const value3 = 3;
  console.log(value3);
  yield value3; // 位置3
  console.log("函数终止运行");
}

//执行生成器函数获取生成器
// 给生成器函数的第一段代码传参,通过在调用生成器函数时,作为实参传入
const generator = foo(1);
// 通过调用生成器的next方法去控制函数执行
console.log(generator.next());
console.log(generator.throw("错误原因"));
//只要在代码生成器函数中有捕获执行throw抛出的异常,生成器可以通过调用next方法,继续执行下面的代码
console.log(generator.next());
console.log(generator.next());

执行结果:

1
{ value: 1, done: false }
捕获到异常 错误原因
第二段代码开始执行
2
{ value: 2, done: false }
3
{ value: 3, done: false }
函数终止运行
{ value: undefined, done: true }
image.png

生成器替换迭代器

之前学习迭代器的时候,我们有封装一个返回一个数组的迭代器的函数

function createArrayIterator(arr) {
  let index = 0;
  return {
    next() {
      if (index < arr.length) {
        return { done: false, value: arr[index++] };
      } else {
        return { done: true, value: undefined };
      }
    },
  };
}

const names = ["lily", "koby"];
const namesIterator = createArrayIterator(names);
console.log(namesIterator.next()); // { done: false, value: 'lily' }
console.log(namesIterator.next()); // { done: false, value: 'koby' }
console.log(namesIterator.next()); // { done: true, value: undefined }

因为生成器是特殊的迭代器,而生成器函数的执行返回一个生成器,所以我们可以用生成器函数,控制对目标数组的遍访

创建一个生成器函数,函数执行返回一个目标数组的生成器

function* createArrayIterator(arr) {
  for (const item of arr) {
    yield item;
  }
}

const names = ["lily", "koby"];
const namesIterator = createArrayIterator(names);
console.log(namesIterator.next()); // { done: false, value: 'lily' }
console.log(namesIterator.next()); // { done: false, value: 'koby' }
console.log(namesIterator.next()); // { done: true, value: undefined }

上面代码可以简写如下:

//创建一个生成器函数,函数执行返回一个目标数组的生成器
function* createArrayIterator(arr) {
  //方法一:
  // for (const item of arr) {
  //   yield item;
  // }

  //方法二:
  // 在生成器函数中使用yield* 后面跟可迭代对象相当于上面代码
  yield* arr;
}
const names = ["lily", "koby"];
const namesIterator = createArrayIterator(names);
console.log(namesIterator.next()); // { done: false, value: 'lily' }
console.log(namesIterator.next()); // { done: false, value: 'koby' }
console.log(namesIterator.next()); // { done: true, value: undefined }

例:创建一个函数,这个函数可以迭代一个范围内的数字

//
function createRangeIterator(start, end) {
  let index = start;
  return {
    next() {
      if (index < end) {
        return { done: false, value: index++ };
      } else {
        return { done: true, value: undefined };
      }
    },
  };
}
const rangeIterate = createRangeIterator(10, 15)
console.log(rangeIterate.next())
console.log(rangeIterate.next())
console.log(rangeIterate.next())
console.log(rangeIterate.next())
console.log(rangeIterate.next())
console.log(rangeIterate.next())

执行结果:

{ done: false, value: 10 }
{ done: false, value: 11 }
{ done: false, value: 12 }
{ done: false, value: 13 }
{ done: false, value: 14 }
{ done: true, value: undefined }

上面可以简写如下

function* createRangeIterator(start, end) {
  let index = start;
  while (index < end) {
    yield index++;
  }
}
const rangeIterate = createRangeIterator(10, 15);
console.log(rangeIterate.next());
console.log(rangeIterate.next());
console.log(rangeIterate.next());
console.log(rangeIterate.next());
console.log(rangeIterate.next());
console.log(rangeIterate.next());

例:自定义类的可迭代性
案例:创建一个Classroom类
教室中有自己的位置,名称,当前教室的学生
这个教室可以进来新学生(add)
创建的教室对象都是可迭代对象


class Classroom {
  constructor(name, address, students) {
    this.name = name;
    this.address = address;
    this.students = students;
  }

  add(stu) {
    this.students.push(stu);
  }

  [Symbol.iterator] = function () {
    let index = 0;
    return {
      next: () => {
        if (index < this.students.length) {
          return { done: false, value: this.students[index++] };
        } else {
          return { done: true };
        }
      },
    };
  };
}

const classroom = new Classroom("测控技术与仪器", "226", [
  "lily",
  "koby",
  "curry",
]);
for (const stu of classroom) {
  console.log(stu);
}

// lily
// koby
// curry

上面代码可简写为:

    this.students.push(stu);
  }

  [Symbol.iterator] = function * () {
    yield* this.students
  };
}

const classroom = new Classroom("测控技术与仪器", "226", [
  "lily",
  "koby",
  "curry",
]);
for (const stu of classroom) {
  console.log(stu);
}

// lily
// koby
// curry

非常感谢王红元老师的深入JavaScript高级语法让我学习到很多 JavaScript 的知识

相关文章

  • 2018-07-16

    ## 1\. 生成器和生成器函数 ``` 生成器的本质就是迭代器 生成器的三种创建办法: 1.通过生成器函数 ...

  • 4.1.列表推导式和生成器的使用 4.22迭代器和可迭代.

    函数生成器1. 函数生成器2. 函数生成器任务同时进行 定义生成器方式 迭代器和可迭代的区别

  • 40.生成器和生成器函数

    生成器是ES6中新增的一种函数控制、使用方案,它可以让我们更加灵活的控制函数什么时候暂停执行、继续执行等平时我们编...

  • ES6:生成器(Generators)

    生成器 先看下面的例子 上面的函数就是生成器函数,和普通函数的不同在于: 使用function*定义生成器函数,而...

  • Promise/generator/async与await

    promise generator 函数 (生成器函数); 普通函数function show(){} 生成器函数...

  • 【第13天】python全栈从入门到放弃

    1. 生成器的3中创建方法 1.通过生成器函数(函数中包含了yield的就是生成器函数,注意:生成器函数被执行. ...

  • tornado协程的工作原理

    包含yield语句的函数是一个生成器。所有的生成器都是异步的。当我们调用生成器函数的时候,生成器函数返回一个生成器...

  • 函数(四)生成器和promise

    1 生成器函数 定义和使用生成器 生成器函数能生成一组值的序列。显式地向生成器请求一个新的值,随后生成器响应一个新...

  • Generator

    生成器函数(Generator Function) 生成器函数不能直接作为函数来使用 执行生成器函数会返回一个生成...

  • ES6 Generators

    生成器函数 生成器函数以function*标注 yield关键字,会暂停生成器的执行,在之后可以继续执行 生成器的...

网友评论

    本文标题:40.生成器和生成器函数

    本文链接:https://www.haomeiwen.com/subject/btlntrtx.html