Generator(一)

文章目录

基本概念

Generator 是ES6提供的一种异步编程的解决方案。Generator 可以理解成一个状态机,封装了多个内部状态。执行Generator函数会返回一个遍历器对象。Iterator

Generator 函数相比于普通函数有两个特征:

  1. function 命令与函数名之间有一个星号(*)
  2. 函数体内使用yield语句定义不同的内部状态
function* hello() {
	yield 'hello';
	yield 'world'
	return 'ending';
}
var hw = hello();
console.log(hw.next()); //{value:'hello', done:false}
console.log(hw.next()); //{value:'world', done:false}
console.log(hw.next()); //{value:'ending', done:false}
console.log(hw.next()); //{value:undefined, done:true}

Generator函数与上一篇的Iterator相同,调用时不会执行,而是生成一个指向内部状态的指针对象。通过调用next()方法,实现指针的后移。

yield

Generator 函数返回的对象只有在调用next()方法时才会遍历下一个内部状态,yield是一种可以暂停执行的函数,yield就是暂停标志。

遍历器对象的next()方法的运行逻辑:

  1. 遇到yield语句就会暂停执行后面的操作,并且将紧跟在yield后面的表达式的值作为返回对象的value属性值。
  2. 下一次调用next()方法时再继续向下执行,直到遇到下一条yield语句。
  3. 如果没有遇到新的yield语句,就一直运行到函数结束,直到return语句为止,并将return语句后面的表达式的值作为返回对象的value属性值,done属性值变为true,表示遍历已结束。
  4. 如果该函数没有return语句,则返回对象的value属性值为undefined。

yield语句和return语句相似之处就是语句后面的表达式的值都作为返回对象的value属性值。不同之处在于return语句只能执行一次,而yield语句可以执行多次。
ps: yield语句只能用在Generator函数中

function* f(){
   yield 1;
   yield 2;
   return 3;
   yield 4;
}
var f = f();
console.log(f.next()); //{value:1, done:false}
console.log(f.next()); //{value:2, done:false}
console.log(f.next()); //{value:3, done:true}
console.log(f.next()); //{value:undefined, done:true}

与Iterator接口的关系

由于Generator 函数就是遍历器生成函数,因此可以把Generator赋值给对象的Symbol.iterator属性,从而使得该对象具有Iterator接口。

var myIterator = {};
myIterator[Symbol.iterator] = function* () { 
	yield 1;
	yield 2;
	yield 3;
 }
 console.log([...myIterator])
 //[1,2,3]

next方法的参数

next方法可以带有一个参数,该参数会被当做上一条yield语句的返回值。

//eg1
function* f(){ 
	for(var i = 0; true; i++){
		var reset = yield i;
		if(reset){
			i = -1;
		}
	}
}
var g = f();
console.log(g.next()); //{value:0, done:false}
console.log(g.next()); //{value:1, done:false}
console.log(g.next(true)); //{value:0, done:false}

//eg2
function* foo(x){
	var y = 2 * (yield (x + 1) );
	var z = yield(y / 3);
	return (x + y + z);
}
var a = foo(5);
console.log(a.next()); //{value:6, done:false}
console.log(a.next()); //{value:NaN, done:false}
console.log(a.next()); //{value:NaN, done:true}

var b = foo(5);
console.log(b.next()); //{value:6, done:false}
console.log(b.next(12)); //{value:8, done:false}
console.log(b.next(13)); //{value:42, done:true}

for…of 循环

for…of 循环可以自动遍历Generator 函数生成的Iterator对象,且此时不再需要调用next()方法。

function* foo(){
	yield 1;
	yield 2;
	yield 3;
	yield 4;
	yield 5;
	return 6;
}
for(let v of foo()){
	console.log(v);
	//1
	//2
	//3
	//4
	//5
}

上面的代码只返回了5条yield语句,并没有返回最后一条return语句。因为done属性值一旦变为true,for…of 循环就会终止,且不包括该返回对象。

for…of 循环遍历对象时,也可以通过Generator函数加上这个接口就可以实现对象的遍历了。

function* object(obj){
	let propKeys = Reflect.ownKeys(obj);

	for(let propkey of propKeys){
		yield [propkey, obj[propkey]];
	}
}

let jane = { first:'Jane', last:'Doe'};
for(let [key,value] of object(jane)){
	console.log(`${key}:${value}`);
	//first:Jane
	//last:Doe
}
上一篇:sleep()、wait()、join()、yield()的区别


下一篇:迭代器与生成器