Generator函数简单记录

506 阅读3分钟

Generator函数运行后会返回一个遍历器对象。 此篇文章仅简单介绍一下,不会有任何复杂例子,详情建议阅读阮一峰<<ES6入门>>Generator 函数的语法

Generator函数结构及运行

函数结构三个注意点:

  • function关键字后面的*号。
  • 内部的yield表达式。
  • return语句可有可无,没有则返回undefined

如下:

function* gen() {
    yield 1;
    yield 2;
    yield 3;
    return 4;
}

运行Generator函数:

  1. 选类似普通函数运行一次,会生成遍历器。
  2. 后续运行遍历器的next()方法,每次会返回{ value: ..., done: [false|true] }对象。
  3. 如果done: false表示遍历未结束,done: true表示遍历结束。
  4. 如果遍历结束后续再调用next()方法将一直返回{ value: undefined, done: true }
let g = gen();
g.next(); // { value: 1, done: false }
g.next(); // { value: 2, done: false }
g.next(); // { value: 3, done: false }
g.next(); // { value: 4, done: true }
g.next(); // { value: undefined, done: true }

在运行过程中,除了第一次之外的后续next()方法可以传入值。

function* gen() {
    let a = yield 1;
    console.log(a);
    yield 2;
}
let g = gen();
g.next(); // { value: 1, done: false }
g.next(10);
// 10
// { value: 2, done: false }
g.next(); // { value: undefined, done: true }

如果Generator函数是某个对象的属性,记得前面的*号。

let obj = {
    * gen() {
        yield 1;
    }
}

Generator原型方法

Generator.prototype.throw()

这个throw方法可在函数体外抛出错误,然后在函数体内捕获。

function* gen() {
    try {
        yield 1;
    } catch(e) {
        console.log(e);
    }
    yield 2;
}

let g = gen();
// 下面的next()记得一定要运行一次,否则只能被外部捕获
g.next(); // { value: 1, done: false }
g.throw('generator error 1');
// generator error 1
// { value: 2, done: false }
g.next(); // { value: undefined, done: true }

Generator.prototype.throw()注意点:

  1. 一个try...catch语句中只能捕获一次错误,如果连续抛出多次错误,只会捕获到第一次。
  2. 记得要先运行第一次next()方法,否则错误只能在函数体外捕获。
  3. 在体外抛出错误后,如果被捕获,会附带执行一次next()方法,不影响下一次遍历。
  4. 注意throw语句与Generator.prototype.throw方法不要混淆。
  5. Generator函数体内抛出的错误,可以被函数体外的try...catch捕获。
  6. 一旦Generator执行过程抛出错误没有被内部捕获,此后调用next()方法,将一直返回{ value: undefined, done: true }

Generator.prototype.return()

返回给定的值,并且终结遍历。

function* gen() {
    yield 1;
    yield 2;
    yield 3;
}
let g = gen();
g.next(); // { value: 1, done: false }
g.return(10); // { value: 10, done: true }
g.next(); // { value: undefined, done: true }

next()throw()return()共同点

作用都是让Generator函数恢复执行,并且使用不同的语句替换yield表达式。

  • next(),替换为对应传入的值,没有传入值则返回undefined
  • throw(),替换为throw语句。
  • return(),替换为return语句。

yield* 表达式

Generator函数体内支持嵌套其他Generator函数,内部会自行遍历,无需手动再遍历。

注意:如果嵌套的Generator函数有返回值,那么在内部必须使用一个变量来存储才可以获取到返回值。

function* gen1() {
    yield 3;
    yield 4;
    return 'gen1';
}
function* gen2() {
    yield 1;
    yield 2;
    let gen1Value = yield* gen1();
    console.log(gen1Value);
    yield 5;
}
let g2 = gen2();
g2.next(); // { value: 1; done: false }
g2.next(); // { value: 2; done: false }
g2.next(); // { value: 3; done: false }
g2.next(); // { value: 4; done: false }
g2.next();
// gen1
// { value: 5; done: false }
g2.next(); // { value: undefined, done: true }

Generator返回的遍历器

Generator函数返回的是一个遍历器,支持下面的操作:

  1. for...of遍历。
  2. 扩展运算符...
  3. 解构赋值。
  4. Array.from()方法。

for...of遍历

function* gen() {
    yield 1;
    yield 2;
    yield 3;
}
for(let i of gen()) {
    console.log(i);
}
// 1
// 2
// 3

扩展运算符...

function* gen() {
    yield 1;
    yield 2;
    yield 3;
}
let a = [...gen()];
// [1, 2, 3]

解构赋值

function* gen() {
    yield 1;
    yield 2;
    yield 3;
}
let [a, b, c] = [...gen()];
a; // 1
b; // 2
c; // 3
let { 0: x, 1: y, 2: z } = [...gen()];
x; // 1
y; // 2
z; // 3

Array.from()

function* gen() {
    yield 1;
    yield 2;
    yield 3;
}
let a = Array.from(gen());
a; // [1, 2, 3]

小结

Generator函数主要几个最基本点:

  • function关键字后面的*号。
  • 函数内部的yield语句。
  • 函数内部用于嵌套其他Generator函数的yield*语句。
  • 函数需要先运行一次后才会返回遍历器,才可以后续使用next()方法。
  • Generator.prototype.throw()
  • Generator.prototype.return()