生成器的秘密

131 阅读3分钟

生成器基础

可以在任意函数声明处使用*从而声明一个生成器函数,生成器函数的执行会产生一个生成器对象,调用生成器函数时,并不执行,只在第一次调用next方法时真正开始执行生成器函数内部语句。在遇到yield时停止。

箭头函数无法声明为生成器函数

关键点

  1. yield关键字可以让生成器停止和开始执行,是一个关键点。
  2. 遇到yield后,执行会停止,函数作用域的状态会被保留。
  3. 想恢复执行,只能在生成器对象上调用next()方法恢复执行。
  4. 返回的情况
    1. yield关键字有点像一个“中间”返回语句,它生成的值会出现在next方法返回的对象里。
    2. 通过yield关键字退出的生成器函数会处于done:false状态
    3. 通过return关键字退出的生成器函数会处于done:true状态
  5. 生成器对象作为可迭代对象

使用yield实现输入和输出

除了可以作为函数的中间返回语句使用,yield关键字还可以作为函数的中间参数使用。next方法的第一个参数会传给yield关键字,但是第一次next方法传入的值不会被使用,因为第一次next是开始执行生成器函数。
  1. 当遇到第一个yield时,第一个yield会将后面的值返回,next方法接受的第一个参数对第一个yield无效;此时返回了第一个yield后面的值
  2. 如果再次执行next方法并传入值,那么生成器内部会接受该值,并且yield 表达式本身会被赋该值,但是返回值依然是yield后面的值。
  3. 额外的,如果yield值后面没有值,那么就代表该中间返回语句没有返回值。
		function *test(){
		    console.log(yield 'a')
		    console.log(yield)
		}
		const d = test()
		
		d.next()
		{value: 'a', done: false}
		
		d.next('c')
		c
		{value: undefined, done: false}

使用*号增强yield行为

function* generatorFn() {
 console.log('iter value:', yield* [1, 2, 3]); 
} 
for (const x of generatorFn()) { 
 console.log('value:', x); 
} 

两种行为

  1. yield * 后面的是可迭代对象,并且等同于使用yield抛出每个值,实质是将一个可迭代对象序列化为一连串单独产出的值,等于循环yield value
  2. yield * 本身返回值是根据生成器函数最终是否return值而定的,如果没有则是undefined

生成器作为默认迭代器

使用增强(* 可迭代对象) 实现 [Symbol iterator] () 方法

提前终止的生成器

终止分为两个情况:提前关闭和错误终止

每个生成器对象都有return方法和throw方法

  1. return 方法会将传入的值作为生成器最后的返回值(done:true,value: return方法传入的值)
  2. throw方法会将传入的值作为错误,在生成器函数内部,如果处理了错误,那么next方法将跳过本次yield,返回的结果是下一次yield的结果。如果没有处理,生成器对象会马上进入closed状态,即被关闭