目录
next 方法参数
yield 表达式本身没有返回值,或者说总是返回 undefined。next 方法可以带一个参数,该参数会被当做上一个 yield 表达式的返回值。
function* f() {
for (var i = 0; true; i++) {
var reset = yield i;
if (reset) {
i = -1;
console.log(i);
}
}
}
let g = f();
console.log(g.next());
console.log(g.next());
console.log(g.next());
console.log(g.next(true));
console.log(g.next());
// {value: 0, done: false}
// {value: 1, done: false}
// {value: 2, done: false}
// -1
// {value: 0, done: false}
// {value: 1, done: false}
function* foo(x) {
var y = 2 * (yield (x + 1));
var z = yield (y / 3);
return (x + y + z);
}
var a = foo(5);
a.next() // Object{value:6, done:false}
a.next() // Object{value:NaN, done:false}
a.next() // Object{value:NaN, done:true}
var b = foo(5);
b.next() // { value:6, done:false }
b.next(12) // { value:8, done:false }
b.next(13) // { value:42, done:true }
解读:
a.next() => x + 1 => 5 + 1 => 6
a.next() => y = 2 * (yield(...)),因为 next 没有参数,yield 表达式返回为 undefined => y = 2 * undefined => y = undefined => (y / 3) => (undefined / 3) => undefined
a.next() => z = yield(...) => z = undefined;(x + y + z) => (5 + undefined + undefined) => undefined
b.next() => x + 1 => 5 + 1 => 6
b.next(12) => y = 2 * (yield(...)),next 有参数,则上一个 yield 表达式返回值为 12 => y = 2 * 12 => y = 24 => (y / 3) = > (24 / 3) => 8
b.next(13) => z = 13 => x + y + z => 5 + 24 + 13 => 42
注意,由于 next 方法的参数代表上一个 yield 表达式的返回值,所以在第一次调用 next 是,传参是没有意义的。V8 引擎直接忽略第一次调用 next 时传递的参数,只有从第二次调用 next 传参才有效。
Generator.prototype.throw()
Generator 函数返回的遍历器对象,都有一个 throw 方法,可以在函数体外抛出异常,在函数体内捕获。
var g = function* () {
try {
yield;
} catch (e) {
console.log('内部捕获', e);
}
};
var i = g();
i.next();
try {
i.throw('a');
i.throw('b');
} catch (e) {
console.log('外部捕获', e);
}
// 内部捕获 a
// 外部捕获 b
如果不调用一次 i.next(),则只会输出 “外部捕获 a”;官方解释:因为第一次调用 next 方法,等同于启动执行 Generator 函数内部代码,否则 Generator 函数还没有开始执行,这是 throw 方法抛出的异常只可能抛出在函数体外部。(个人理解:不调用一次 next 方法,遍历器指针指向函数体头部,还未进入 Generator 函数 try catch 模块,所以未能在函数体内部捕获异常)
throw 方法可以接受一个参数,该参数会被 catch 语句接收。
遍历器对象的 throw 和全局的 throw 方法是不一样的。如果上面代码是用 throw new Error("a"); 抛出的,则 Generator 函数内部不能捕获到这个异常。
如果 Generator 函数体内部没有部署 try...catch 代码块,则遍历器对象 throw 方法抛出的异常将会被外部 try...catch 捕获。
如果 Generator 内部和外部都没有部署 try...catch 方法,遍历器对象 throw 方法会让程序直接报错中断进行。
throw 方法被 Generator 函数内部捕获后,会附带执行下一条 yield 表达式。也就说说会附带执行一次 next 方法。
var gen = function* gen() {
try {
yield console.log('a');
} catch (e) {
// ...
}
yield console.log('b');
yield console.log('c');
}
var g = gen();
g.next() // a
g.throw() // b
g.next() // c
如果遍历器指针指向的位置不在 try...catch 内部了,在发生异常不会被 Generator 函数内部捕获。
var gen = function* gen() {
try {
yield console.log('a');
} catch (e) {
// ...
}
yield console.log('b');
yield console.log('c');
}
var g = gen();
g.next()
g.next()
g.throw() // 直接报错中止
throw 命令和 throw 方法是无关的,两者互不影响。
var gen = function* gen(){
yield console.log('hello');
yield console.log('world');
}
var g = gen();
g.next();
try {
throw new Error();
} catch (e) {
g.next();
}
// hello
// world
一旦 Generator 执行过程中抛出异常,且没有被内部捕获,就不会在执行下去了。如果在调用 next 方法,则会返回 {done: true, value: undefined},即 JavaScript 引擎认为此 Generator 函数已经运行结束。
function* g() {
yield 1;
console.log('throwing an exception');
throw new Error('generator broke!');
yield 2;
yield 3;
}
function log(generator) {
var v;
try {
v = generator.next();
console.log('第一次运行next方法', v);
} catch (err) {
console.log('捕捉错误', v);
}
// 第一次运行next方法 {value: 1, done: false}
try {
v = generator.next();
console.log('第二次运行next方法', v);
} catch (err) {
console.log('捕捉错误', v);
}
// throwing an exception
// 捕捉错误 {value: 1, done: false}
try {
v = generator.next();
console.log('第三次运行next方法', v);
} catch (err) {
console.log('捕捉错误', v);
}
// 第三次运行next方法 {value: undefined, done: true}
}
log(g());
Generator.prototype.return()
返回给定的值,并终止遍历 Generator 函数。
function* gen() {
yield 1;
yield 2;
yield 3;
}
var g = gen();
g.next() // { value: 1, done: false }
g.return('foo') // { value: "foo", done: true }
g.next() // { value: undefined, done: true }
如果调用 return 方法时不传参数,则返回值的 value 为 undefined。
function* gen() {
yield 1;
yield 2;
yield 3;
}
var g = gen();
g.next() // { value: 1, done: false }
g.return() // { value: undefined, done: true }
如果 Generator 函数内部有 try...finally 代码块,且正在执行 try 代码块,那么 return() 方法会导致立刻进入 finally 代码块,执行完以后,整个函数才会结束。
function* numbers () {
yield 1;
try {
yield 2;
yield 3;
} finally {
yield 4;
yield 5;
}
yield 6;
}
var g = numbers();
g.next() // { value: 1, done: false }
g.next() // { value: 2, done: false }
g.return(7) // { value: 4, done: false }
g.next() // { value: 5, done: false }
g.next() // { value: 7, done: true }
上面代码,调用 return() 方法后,就开始执行 finally 代码块,不执行 try 剩下的代码了,等到 finally 代码块执行完,再返回 return() 方法指定的返回值。
next()、throw()、return() 的共同点
让 Generator 函数恢复执行,并使用不同的语句替换 yield 表达式。
next() 是将表达式替换成一个值。
throw() 是将表达式替换成一个 throw 语句。
return() 是将表达式替换成一个 return 语句。