Generator函数的基本语法介绍

Generator基本概念

Generator是ES6提供的一种异步的编程解决方案。它的行为特点就是,在function关键字和函数名之间有一个 * 号。在函数的内部,使用yield关键字,标识不同的状态。

Generator函数,也是一种函数,不过这种函数在它的内部,封装了多个状态机,并且这个函数的执行结果,返回的是一个遍历器对象,这个遍历器对象可以通过其next()方法,得到这个Generator函数的每一种状态。

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

上面示例中,x是generator函数的返回值,也就是一个遍历器对象,这个返回值调用next()方法,然后会依次输出一个带有yield返回值的一个对象。

关于遍历器对象,调用的next()方法的返回值是一个对象,对象还有一个属性是done,表示这个遍历器对象是否遍历完毕。

遍历器对象的next方法的执行逻辑

  • 从上到下依次执行generator函数的,直到遇到第一个yield关键字,就暂停执行,并且会把yield关键字后面的表达式作为返回值。
  • yield关键字后面的表达式的值,会作为next函数的返回对象的value值。
  • 如果这个遍历器对象还没有执行完毕,那么next函数的返回对象的done的值就是false,如果执行到retrun,或者这个函数执行完毕,这个done值就是true。

generator函数,可以不用yield语句,在这样的情况下,形成一个暂停执行函数,需要继续调用next方法,才能真正的执行完毕这个函数。

yield语句只能放到generator函数的内部使用,在其他任何地方都是错误的。

在一个表达式中,如果包含yield,这个yield表达式必须使用圆括号括起来。

function * add(){
    return 1 + yield 1;//这种写法错误
    return 1 + (yield 1);//这种写法是对的
}

与Iterator接口的关系

任何一个对象的的 Symbol.iterator 方法,都是一个遍历器生成函数,调用该函数,会返回一个该对象的遍历器对象。而generator函数的函数返回值就是一个遍历器对象。所以可以把一个generator函数赋值到对象的Symbol.iterator属性上面。

var obj = { name: 'mapbar_front' };
obj[Symbol.iterator] = function * (){
    yield 1;
    yield 2;
    yield 3;
}
console.log([...obj]);//[1,2,3]

generator函数的执行结果是一个遍历器对象,这个遍历器对象,也拥有 Symbol.iterator 属性。也是一个遍历器对象生成函数,调用这个函数,返回的是自身。

function gen * (){
    yield 1
}
var g = gen();
g[Symbol.iterator]() === g;
//true

next方法的参数

yield语句本身没有返回值,或者可以理解为返回值总是undefined,当next携带参数之后,这个参数可以当作上依次yield句的返回值。

function * do(){
    for(var i = 0; true; i++){
        var result = yield i;
        if(result) {
            i = -1;
        }
    }
}
var Do = do();
Do.next();//0
Do.next();//1
Do.next(true);//0

下面是另一个关于generator函数的示例,这个示例说明,我们可以在一个运行中的函数,添加不同的参数,从而得到不同的运算结果。这相对于之前的那些JavasScript特性,有很大的功能上的突破。

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(3));//{ value: 2, done: false }
console.log(b.next(4));//{ value: 15, done: true }

for of循环

generator函数的返回值,是一个遍历器对象,可以被for of循环遍历。

function* foo(){
    yield 1;
    yield 2;
    yield 3;
    yield 4;
    yield 5;
}
var obj = foo();
for(var i of obj){
    console.log(i);
}
//1,2,3,3,4,5

真对对象,我们想要进行for of循环,一般的方式肯定是不行的,但是我们可以借助generator函数达到我们的目的。

function* objectEntries(obj) {
    let propKeys = Reflect.ownKeys(obj);
    for (var propKey of propKeys) {
        yield [propKey, obj[propKey]];
    }
}
var obj = {
    name: 'mapbar_front',
    age: 28,
};
for (var [key, value] of objectEntries(obj)) {
    console.log(`${key},${value}`);
}

也可以通过对象的Symbol.iterator的属性上,然后添加一个遍历的接口。

function* objectEntries(obj) {
    let propKeys = Reflect.ownKeys(obj);
    for (var propKey of propKeys) {
        yield [propKey, obj[propKey]];
    }
}
var obj = {
    name: 'mapbar_front',
    age: 28,
};
obj[Symbol.iterator] = objectEntries;
for (var [key, value] of obj) {
    console.log(`${key},${value}`);
}

除了for of循环,对于扩展运算符 …,解构赋值,Array.from方法,在他们的内部都是调用遍历器接口,所以我们可以把generator函数的返回结果用于它们身上。

function * objEntries() {
    yield 1;
    yield 2;
    return 3;
    yield 4;
}
var a = objEntries();
console.log([...a]);
console.log(Array.from(objEntries()));
var [x,y] = objEntries();
console.log(x,y);
for(var i of objEntries()){
    console.log(i)
}

Generator.prototype.throw方法,错误的抛出和捕获。

每个generator函数,执行后有一个遍历器对象,这个对象有一个throw方法,用来错误的抛出。

这个throw出来的错误,可以在generator函数内部捕获到,但是再次抛出错误,只能在外部catch语句中捕获。

throw方法可以携带一个参数,这个参数可以在catch中捕获。一般而言,这个throw方法的参数是一个Error对象。

var g = function* () {
    try {
        yield;
    } catch (e) {
        console.log('内部捕获', e);
    }
};
var i = g();
i.next();

try {
    i.throw('a');
    i.throw('b');
} catch (e) {
    console.log('外部捕获', e);
}

遍历器对象的throw方法抛出的错误,如果想要被捕获,必须执行至少一次next方法。

遍历器对象的throw方法被捕获,会顺带执行下一次的next方法。

var g = function* () {
    try {
        yield console.log('a');
        yield console.log('b');
    } catch (e) {
        console.log('内部捕获', e);
    }
    yield console.log('c');
};
var i = g();
i.next();
i.throw(new Error('出错了!'));

Generator.prototype.return方法

这个方法的作用,就是对遍历器对象的遍历进行终结操作的。可以返回一个给定的值,当这个方法被调用之后,next方法不会在进行yield语句的调用操作。

generator函数,调用return方法后,如果内部使用try finally模块,会等到finally模块的东西执行完毕之后,再执行return方法。

function* g() {
   yield 1;
   yield 2;
   yield 3;
}
var i = g();
console.log(i.next());
console.log(i.return('aa'));

在generator函数中调用另一个generator函数

一般而言,直接调用的时候,是默认没有任何效果的,我们使用的是 yield* 来进行调用。

function* bar(){
    yield 1;
    yield* foo();
    yield 2;
}
function* foo(){
    yield 3;
    yield 4;
}
for(let i of bar()){
    console.log(i);
}
// 1, 3, 4, 2

猜你喜欢

转载自blog.csdn.net/mapbar_front/article/details/81165638