generator

generator(生成器)是ES6标准引入的新的数据类型。一个generator看上去像一个函数,但可以返回多次。

generator跟函数很像,定义如下:

function* foo(x) {
    yield x + 1;
    yield x + 2;
    yield x + 3;
}

除了return语句,还可以用yield返回多次。yield返回{value:xx,done:false},每次的结果存在value中。

直接调用试试:

1
foo(5); // fib {[[GeneratorStatus]]: "suspended", [[GeneratorReceiver]]: Window}

直接调用一个generator和调用函数不一样,fib(5)仅仅是创建了一个generator对象,还没有去执行它。

generator方法调用

不断地调用generator对象的next()方法:
var f = foo(1)
f.next()
Object {value: 2, done: false}
f.next()
Object {value: 3, done: false}
f.next()
Object {value: 4, done: true} //已经返回true,不用再返回了
f.next()
Object {value: undefined, done: true}

next()方法会执行generator的代码,然后,每次遇到yield x;就返回一个对象{value: x, done: true/false},然后“暂停”。

直接用for … of循环迭代generator对象,这种方式不需要我们自己判断done:
for (var x of foo(1)) {
    console.log(x); // 依次输出2,3,4
}
直接将对象中的value依次返回出来

来封装一个持久输出1~100的函数

function* increase(){
    for(var i=1;i<101;i++){
        yield i
    }
}

var f = increase()
for(var i = 1;i<101;i++){
    console.log( i == f.next().value ) //100个true
}

yield*

在一个generator中调用其他的Generator方法,需用用

function* foo() {
    yield 0;
    yield 1;
}
function* bar() {
    yield 'x';
    yield* foo();
    yield 'y';
}
for (let v of bar()){
    console.log(v);
};
//依次输出 x,0,1,y

next()方法的参数

如果给next方法传参数, 那么这个参数将会作为上一次yield语句的返回值,因为在执行异步代码以后, 有时候需要上一个异步的结果,作为下次异步的参数,如此循环:

function* foo(x) {
    var y = 2 * (yield (x + 1));
    var z = yield (y / 3);
    return (x + y + z); //或者 yield (x + y + z)
}
var a = foo(5);
a.next() // Object{value:6, done:false}
a.next() // Object{value:NaN, done:false}
a.next() // Object{value:NaN, done:true}
var b = foo(5);
b.next() // { value:6, done:false }
b.next(12) // { value:8, done:false }
b.next(13) // { value:42, done:true }

注意

  • 申明方式 function * A(){}
  • 直接调用generate函数只是创建一个generate对象,而没有执行内部代码
  • 调动f.next(),只是循环在generate对象中执行操作,遇到yield则返回,下次调用next()紧接着执行,直到遇到下一个yield或者return 返回
  • 如果给next方法传参数, 那么这个参数将会作为上一次yield语句的返回值
  • 如果执行return()方法, 那么这个迭代器的返回会被强制设置为迭代完毕, 返回{value:xx,done:true}