这是我参与更文挑战的第12天,活动详情查看:更文挑战
Generator生成器,也叫做生成器函数,它本质上是函数,但是它可以返回多次。Generator是ES6标准引入的新的数据类型。
概念
生成器是一个普通函数,特征在于:
- function关键字与函数名之间有一个星号(*),
function*
- 函数体内部使用yield表达式,定义不同的内部状态(yield在英语里的意思就是“产出”)。
- 函数体内部使用return,可以返回给定的值,并且终结遍历Generator函数。
function* foo(x) {
yield x + 1;
yield x + 2;
return x + 3;
yield x + 4;
}
复制代码
生成器函数与普通函数对比:
- 都是通过括号()执行。
- 调用生成器函数后,函数并不立即执行,返回的不是函数运行结果,而是一个迭代器对象。
- 通过迭代器对象
next
方法分段执行函数,获取函数内部yield
产出值。
const r = foo(0);
r.next();
// { value: 1, done: false }
r.next();
// { value: 2, done: false }
r.next();
// { value: 3, done: true }
r.next();
// { value: undefined, done: true } 被return终结了
r.next();
// { value: undefined, done: true }
复制代码
yield表达式
yield
只能用在生成器函数中,在普通函数或者其它地方使用都会报语法错误。
yield
在生成器内部表示暂停标志,对调用的地方来说表示产出值。
yield
表达式与return
语句既有相似之处,也有区别。
- 相似之处在于,都能返回紧跟在语句后面的那个表达式的值。
- 区别在于每次遇到
yield
,函数暂停执行,下一次再从该位置继续向后执行,而return
语句不具备位置记忆的功能。 - 一个函数里面,只能执行一次(或者说一个)
return
语句,但是可以执行多次(或者说多个)yield
表达式。 - 正常函数只能返回一个值,因为只能执行一次
return
;Generator
函数可以返回一系列的值,因为可以有任意多个yield
。
next方法
yield
表达式本身没有返回值,或者说总是返回undefined
。next
方法可以带一个参数,该参数就会被当作上一个yield
表达式的返回值。
Generator函数从暂停状态到恢复运行,它的上下文状态(context)
是不变的。通过next
方法的参数,就有办法在Generator函数开始运行之后,继续向函数体内部注入值。也就是说,可以在Generator函数运行的不同阶段,从外部向内部注入不同的值,从而调整函数行为。
function* dataConsumer () {
console.log('Started');
console.log(`1. ${yield}`);
console.log(`2. ${yield}`);
return 'result';
}
// 等效代码
function* dataConsumer () {
console.log('Started');
let result = yield;
console.log(`1. ${result}`);
result = yield;
console.log(`2. ${result}`);
return 'result';
}
let genObj = dataConsumer();
genObj.next(); // {value: undefined, done: false}
// Started
genObj.next('a'); // {value: undefined, done: false}
// 1. a
genObj.next('b'); // {value: "result", done: false}
// 2. b
复制代码
for…of循环
for...of
循环可以自动遍历 Generator函数运行时生成的Iterator
对象,且此时不再需要调用next方法。
function* foo () {
yield 1;
yield 2;
yield 3;
yield 4;
yield 5;
return 6;
}
for (const v of foo()) {
console.log(v);
}
复制代码
注意,一旦next
方法的返回对象的done属性为true,for...of
循环就会中止,且不包含该返回对象,所以上面代码的return语句返回的6,不包括在for...of
循环之中。
但是yield 5之后的代码是会被执行。
异常
Generator函数返回的遍历器对象都有一个throw
方法,可以在函数体外抛出错误,然后在Generator函数体内捕获。
const g = function* () {
try {
yield;
} catch(e){
console.log('内部捕获', e);// 捕获i对象的第一个throw
}
};
const i = g();
i.next();
try {
i.throw('a');// 在函数体外抛出错误,在函数体内捕获错误
i.throw('b');// 在函数体外抛出错误,在函数体外捕获错误
} catch (e) {
console.log('外部捕获', e);
}
// 结果
// 内部捕获 a
// 外部捕获 b
复制代码
© 版权声明
文章版权归作者所有,未经允许请勿转载。
THE END