es6迭代器iterator和生成器generator3-迭代器和生成器的高级用法

34次阅读

共计 2810 个字符,预计需要花费 8 分钟才能阅读完成。

前面 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: 委托生成器(组合生成器或者生成器组合?)

正文完
 0