return和throw在生成器中起到了终止的作用,生成器原型那篇文章里介绍了Chrome浏览器只实现了throw,没有实现return。所以我们先看看throw是怎么用:
function *foo() {
yield 1;
yield 2;
yield 3;
}
var it = foo();
console.log(it.next()); // { value: 1, done: false }
try {
it.throw( new Error("Oops!") );
}
catch (err) {
console.log(err); // Error: Oops!
}
console.log(it.next()); // { value: undefined, done: true }
function* genFunc1() {
try {
console.log('Started');
yield 1;
yield 2;
} catch (error) {
console.log('Caught: ' + error);
}
}
let genObj1 = genFunc1();
console.log(genObj1.next());//Object {value: 1, done: false}
genObj1.throw(new Error('Problem!'))
genObj1.next();//Object {value: undefined, done: true}
假如我们不捕获异常会怎么样:
function* genFunc1() {
console.log('Started');
yield 1;
yield 2;
}
let genObj1 = genFunc1();
console.log(genObj1.next());
genObj1.throw(new Error('Problem!'))//这里直接抛出异常
console.log(genObj1.next());//这里不再执行
最后看个更详细的例子:
function *foo() {
try {
yield 1;
}
catch (err) {//catch异常的yield,下面的yield还可以继续执行
console.log( err );
}
yield 2;
throw "Hello!";
}
var it = foo();
it.next(); // { value: 1, done: false }
try {
it.throw( "Hi!" ); // Hi!
// { value: 2, done: false }
it.next();
console.log( "never gets here" );
}
catch (err) {
console.log( err ); // Hello!
}
由于Chrome中没有实现return,我们可以自己简单实现一下,return的语义就是终结生成器,done设置成true,value设置成return传入的参数值。
let Generator_prototype = Object.getPrototypeOf(function* (){}).prototype;
Generator_prototype.return = function (val) {
[...this];//spread操作符让生成器执行完
return {value:val,done:true};
};
function* genFunc1() {
try {
console.log('Started');
yield 1;
yield 2;
} finally {
console.log('Exiting');
}
}
let genObj1 = genFunc1();
console.log(genObj1.next());
console.log(genObj1.return('Result'));
console.log(genObj1.next());
/*
Started
Object {value: 1, done: false}
Exiting
Object {value: "Result", done: true}
Object {value: undefined, done: true}
*/
但是上面这种简单实现有个严重的问题就是不能处理无限的生成器,例如:
function* genFunc(){
for(let i=0;;i++){
yield i;
}
}
//如果调用return会导致浏览器崩溃
function* genFunc2() {
try {
console.log('Started');
yield;
} finally {
yield 'Not done, yet!';
}
}
上面这种写法,调用return时应该是不终止生成器,在下一次调用next的时候在终止。但上面在finally里写yield的做法十分不推荐,本身finally的意思就是在终止之前进行最后的处理,finally是最后一步。但是把yield放在里面导致了生成器不能结束,finally也失去了其意义。
我还没有想到一个完美的解决方法处理上面的问题,有兴趣的同学可以自己想想。
*以上全部代码在Chrome 48下通过测试