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