前面2篇系列文章讲解了迭代器和生成器的最常用,最基础的用法;这篇来讨论迭代器和生成器的一些稍稍高级一点的用法:

1: 给迭代器的next()方法传参2: 从迭代器中抛出错误3: 在生成器中使用return语句4: 委托生成器(组合生成器或者生成器组合?)

1: 给迭代器的next()方法传参
在前面2篇系列文章中我们使用的next()方法都是没有传参的,调用next()会依次返回迭代器里面的值。但是,实际上我们是可以给next()方法传参数的,那在这种情况下我们会得到什么样的结果呢?

function* createIterator() {    let first = yield 1;    console.log(`first: ${first}`);    let second = yield first + 2;    console.log(`second: ${second}`);    let third = yield second + 3;}let iterator = createIterator();console.log(iterator.next(0));console.log(iterator.next(4));console.log(iterator.next(5));console.log(iterator.next());

我们得到下面的输出结果:

{value: 1, done: false}first: 4{value: 6, done: false}second: 5{value: 8, done: false}{value: undefined, done: true}

在第二次和第三次的next调用中我们分别传入参数4和5,而4和5也分别被赋值给了变量first和second。当我们执行:
iterator.next(4)的时候,生成器内部执行的代码实际上是:

let first = 4; yield 4 + 2; //所以我们得到 {value: 6, done: false}

iterator.next(5)的时候,生成器内部执行的代码实际上是:

let second = 5; yield 5 + 3; //所以我们得到 {value: 8, done: false}

看下面一个图或许能更直观一些:

        以上截图来自书 Understanding ECMAScript 6

但是上面的例子中,我们也看到一个有趣的现象,就是我们第一次调用next的时候,是传了参数0的iterator.next(0),但是我们依然得到了结果{value: 1, done: false}。这是因为第一次调用next(),无论传递什么参数总是会被丢弃,所以给第一次调用的next()方法传值是无意义的。
或许你看到这里也还不是完全明白了给next()传参时,生成器内部到底是怎样工作。接下来我们再看一个例子,这一次我们在第3次调用next()的时候,不传参数,看会发生什么:

function* createIterator() {    let first = yield 1;    console.log(`first: ${first}`);    let second = yield first + 2;    console.log(`second: ${second}`);    let third = yield second + 3;}let iterator = createIterator();console.log(iterator.next(0));console.log(iterator.next(4));console.log(iterator.next());console.log(iterator.next());

我们得到的输出结果是:

{value: 1, done: false}first: 4{value: 6, done: false}second: undefined{value: NaN, done: false}{value: undefined, done: true}

在第三次的next调用中,我们并没有传递任何参数,生成器内部的执行情况就是:

let second; yield undefined + 2; //所以我们得到结果{value: NaN, done: false}

其实从这里例子我们也可以看出,在生成器内部,yield执行的结果并不会被保存下来赋值给内部的变量,例如这里我们在第三次没有给next()传递参数,那么second的值就是undefined,而不是第二次yield执行结果的value 6。

2: 从迭代器中抛出错误
我们知道一般的函数的执行结果有2种:

1: 返回一个值2: 抛出一个错误

生成器函数作为一种特殊的函数,但是它本身也是函数,所以它也可以抛出错误。只是它抛出错误的时间与一般函数不同,看一下下面的代码:

function* createIterator() {    let first = yield 1;    let second = yield first + 2;    yield second + 3;}let iterator = createIterator();console.log(iterator.next()); //{value: 1, done: false}console.log(iterator.next(4)); //{value: 6, done: false}console.log(iterator.throw(new Error('Boom'))); //Uncaught Error: Boom

在生成器内部代码执行情况如下图所示:

        以上截图来自书 Understanding ECMAScript 6

当我们抛出错误之后,代码就停止了。let second语句并不会被执行到。
但是生成器里面的throw()它会像yield一样,也会返回一个结果。我们可以像一般函数一样catch这个错误,并且之后的代码依然可以得到执行:

function* createIterator() {    let first = yield 1;    let second;    try {        second = yield first + 2;    } catch (error) {        second = 6;    }    yield second + 3;}let iterator = createIterator();console.log(iterator.next()); //{value: 1, done: false}console.log(iterator.next(4)); //{value: 6, done: false}console.log(iterator.throw(new Error('Boom'))); //{value: 9, done: false}console.log(iterator.next());//{value: undefined, done: true}

在这个例子中,我们catch了错误之后,给second赋值6,它后面的代码yield second + 3;也依然可以得到执行。
3: 在生成器中使用return语句

4: 委托生成器(组合生成器或者生成器组合?)