JavaScript 生成器函数
一、function*
function*
这种声明方式会定义一个生成器函数(generator function),它返回一个Generator
(生成器)对象。
生成器函数在执行时能暂停,之后又能从暂停处继续执行。调用生成器函数并不会马上执行函数中的语句,而是返回一个生成器的迭代器(iterator)对象。
每次调用迭代器的next()
方法时,函数中的语句会从开始(首次调用next()
)或前一个yield
执行到下一个yield
的位置为止,yield
后紧跟迭代器要返回的值。(如果使用的是yield*
,则表示暂停当前生成器的执行并将执行权移交给另一个生成器函数。)
next()
方法返回一个包含value
和done
属性的对象,其中value
表示本次yield
表达式的返回值,done
为布尔类型,表示生成器后续是否还有yield
语句(即生成器函数是否执行完成并返回)。调用next()
方法时,如果传入了参数,那么这个参数会作为前一条执行的yield
语句的返回值。
如果在生成器函数中显示return
时,此时会立即将生成器变为完成状态(即next()
方法返回的对象的done
属性值为true)。如果return
语句后有值,则该值会作为next()
方法返回的对象的value属性值。
注意:生成器函数不能当做构造函数使用()。
1、function*
声明
- 语法
function* name([param1[, param2[, ...paramN]]]){
}
其中name为函数名,param为传递给函数的参数,一个函数最多可以有255个参数。
- 示例一
function* test(){
yield 10;
let y = yield 'foo';
console.log(`y=${y}`);//y=5
yield y;
console.log(`y=${y}`);//y=5
return 'hello world!';
yield 'unreachable';//不会被执行
}
let gen = test();
//执行yield 10,返回10
console.log(gen.next());//{value: 10, done: false}
//执行yield 'foo',返回'foo'
console.log(gen.next());//{value: "foo", done: false}
//将5作为前一个`yield`语句的返回值,即执行let y = 5,返回5
console.log(gen.next(5));//{value: 5, done: false}
//执行完毕
console.log(gen.next());//{value: "hello world!", done: true}
y=5
{value: 5, done: false}
y=5
- 示例二
function* genFnTest(){
let first = yield 1;
let second = yield first + 2;
yield second + 3;
}
let gen = genFnTest();
console.log(gen.next());//{value: 1, done: false}
console.log(gen.next(4));//{value: 6, done: false}
console.log(gen.next(5));//{value: 8, done: false}
console.log(gen.next());//{value: undefined, done: true}
- 示例三
function* foo(){
yield 'a';
yield 'b';
yield 'c';
}
let str = '';
for(let val of foo()){
str += val;
}
console.log(str);//abc
- 示例四
function* idMaker(){
let index = 0;
while(index < 3){
yield index++;
}
}
let gen = idMaker();
console.log(gen.next());//{value: 0, done: false}
console.log(gen.next());//{value: 1, done: false}
console.log(gen.next());//{value: 2, done: false}
console.log(gen.next());//{value: undefined, done: true}
带参数的生成器:
function* idMaker(){
let index = arguments[0] | 0;
let max = index + 3;
while(index < max){
yield index++;
}
}
let gen = idMaker(5);
console.log(gen.next());//{value: 5, done: false}
console.log(gen.next());//{value: 6, done: false}
console.log(gen.next());//{value: 7, done: false}
console.log(gen.next());//{value: undefined, done: true}
- 示例五
将多维数组转为一维数组:
function* iterArr(arr){
if(Array.isArray(arr)){
for(let i = 0; i < arr.length; i++){
yield * iterArr(arr[i]);
}
}else{
yield arr;
}
}
//使用for-of
let arr = ['a', ['b', 'c'], ['d', 'e']];
let ret = [];
for(let x of iterArr(arr)){
ret.push(x);
}
console.log(ret);//["a", "b", "c", "d", "e"]
//直接将迭代器展开
arr = ['a', ['b',[ 'c', ['d', 'e']]]];
let gen = iterArr(arr);
ret = [...gen];
console.log(ret);//["a", "b", "c", "d", "e"]
2、function*
表达式
- 语法
function* [name]([param1[, param2[, ...paramN]]]){
}
与function*
声明类似,其中函数名name在声明匿名函数时可以省略。
- 示例
var x = function*(y){
yield y * y;
}
二、yield
与yield*
1、yield
yield关键字用来暂停或恢复一个生成器函数。
- 语法
[rv] = yield [expression];
如果省略expression
,则返回undefined
。
-
说明
生成器函数执行遇到
yield
表达式时,函数代码将被暂停,直到生成器的next()
方法被调用。每次调用生成器的next()
方法时,生成器函数都会恢复执行,直到达到以下某个值:-
遇到
yield
语句导致生成器再次暂停并返回生成器的新值
-
遇到
throw
语句当抛出异常时,生成器完全停止执行,在调用者中继续执行,同正常情况下的异常抛出
-
遇到
return
语句生成器执行结束,将
IteratorResult
返回给调用者,返回对象的value
属性值为return
的值,done
属性值为true -
到达生成器函数的结尾(无
return
)生成器执行结束,将
IteratorResult
返回给调用者,返回对象的value
属性值为undefined
,done
属性值为true
-
2、yield*
yield*
表达式用于代理另一个生成器或可迭代对象。(用来在一个生成器函数中执行另一个生成器函数,直接调用是没用的。)
- 语法
yield* [[expression]];
其中expression
表达式须返回一个可迭代的表达式。
yield*
表达式本身的值是当迭代器关闭时(done
属性值为true时)返回的值。
- 示例一
function* generator(i){
yield i + 1;
yield i + 2;
yield i + 3;
}
function* anotherGenerator(i){
yield i;
yield* generator(i);//移交执行权
yield i + 10;
}
let gen = anotherGenerator(10);
console.log(gen.next());//{value: 10, done: false}
console.log(gen.next());//{value: 11, done: false}
console.log(gen.next());//{value: 12, done: false}
console.log(gen.next());//{value: 13, done: false}
console.log(gen.next());//{value: 20, done: false}
console.log(gen.next());//{value: undefined, done: true}
- 示例二
yield*
function* generator(){
yield* [1, 2];
yield* "34";
yield* arguments;
}
let gen = generator(5, 6);
console.log(gen.next());//{value: 1, done: false}
console.log(gen.next());//{value: 2, done: false}
console.log(gen.next());//{value: "3", done: false}
console.log(gen.next());//{value: "4", done: false}
console.log(gen.next());//{value: 5, done: false}
console.log(gen.next());//{value: 6, done: false}
console.log(gen.next());//{value: undefined, done: true}
- 示例三
function* generator(){
yield* [1, 2, 3];
return 'foo';
}
let ret;
function* anotherGenerator(){
ret = yield* generator();
}
let gen = anotherGenerator();
console.log(gen.next());//{value: 1, done: false}
console.log(gen.next());//{value: 2, done: false}
console.log(gen.next());//{value: 3, done: false}
console.log(ret);//undefined
console.log(gen.next());//{value: undefined, done: true}
console.log(ret);//foo
三、生成器方法
1、next()
next()
方法返回一个包含value
和done
属性的对象。
- 语法
gen.next(value)
其中value为向生成器传递的值。
- 示例
function* gen(){
yield 1;
let ret = yield 2;
return ret;
}
let g = gen();
console.log(g.next());//{value: 1, done: false}
console.log(g.next());//{value: 2, done: false}
console.log(g.next('hello world'));//{value: "hello world", done: true}
2、return()
返回给定的值并结束生成器。
- 语法
gen.return(value)
其中value为需要返回的值。
如果对已完成的生成器调用return(value)
,生成器仍保持完成状态。如果return()
方法没有参数,则返回对象与next()
方法返回相同;否则,将方法参数设置为返回对象的value
属性值。
- 示例
function* gen(){
yield 1;
yield 2;
yield 3;
}
let g = gen();
console.log(g.next());//{value: 1, done: false}
console.log(g.return('foo'));//{value: "foo", done: true}
console.log(g.next());//{value: undefined, done: true}
console.log(g.return());//{value: undefined, done: true}
console.log(g.return('bar'));//{value: "bar", done: true}
3、throw()
此方法向生成器抛出异常,并恢复生成器的执行,返回带有value
和done
属性的对象。
- 语法
gen.throw(exception)
其中exception为抛出的异常,建议抛出Error
对象的实例。
- 示例一
function* gen(){
try{
yield 'foo';
yield 'bar';
}catch(e){
console.log('Error caught!');
}
yield 'foo bar';
}
let g = gen();
console.log(g.next());
console.log(g.throw(new Error('Something went wrong!')));
输出:
{value: "foo", done: false}
Error caught!
{value: "foo bar", done: false}
- 示例二
function* gen(){
let x = 0;
while(x < 4){
try{
yield x++;
}catch(e){
console.log(`Caughted Error: ${e.message}`);
}
}
}
let g = gen();
try{
console.log(g.next());//
console.log(g.throw(new Error('wrong!')));//
console.log(g.next());//
console.log(g.throw(new Error('error!')));//
console.log(g.next());//
console.log(g.throw(new Error('xxx!')));//
console.log('Will not be executed');
}catch(e){
console.log('out caught...', e.message);
}
console.log('finish');
输出:
{value: 0, done: false}
Caughted Error: wrong!
{value: 1, done: false}
{value: 2, done: false}
Caughted Error: error!
{value: 3, done: false}
{value: undefined, done: true}
out caught... xxx!
finish