//{value:3, done:false}
console.log(iterator2.next());
//{value:undefined, done:true}
注:yield 关键字只能用于生成器内部,用于其他位置会出现语法错误,即使在生成器内部的函数中也不行,下面的代码报错 。
//有效
function* validGeneratorFn(){
yield;
}
//报错
function* invalidGeneratorFnA(){
function a(){
yield;
}
}
//报错
function* invalidGeneratorFnB(){
const b=()=>{
yield;
}
}
//报错
function* invalidGeneratorFnc(){
(()=>{
yield;})();
}
yield作为函数中间参数使用
使用 yield 实现输入和输出, yield 关键字还可以作为函数的中间参数使用,上一次让生成器函数暂停的 yield 关键字会接收到传给 next() 方法的第一个值 。第一次调用 next() 传入的值不会被使用,因为这一次调用是为了开始执行生成器函数
function* generatorFn(initial) {
console.log(initial);
console.log(yield);
console.log(yield);
}
let generatorObject = generatorFn(1);
generatorObject.next(2); // 1
generatorObject.next(3); // 3
generatorObject.next(4); // 4
yield关键字同时用于输入输出
// yield 关键字可以同时用于输入和输出,如下
function* generatorFn() {
return yield 1;
}
let generatorObject = generatorFn();
console.log(generatorObject.next());
// { done: false, value: 1 }
console.log(generatorObject.next(2));
// { done: true, value: 2 }
因为函数必须对整个表达式求值才能确定要返回的值,所以它在遇到yield关键字时暂停执行并计算出要产生的值:1。下一个调用next()传入了 2,作为交给了同一个yield的值 。然后这个值被确定为本次生成器函数要返回的值
使用yield实现递归算法
yield*最有用的地方是实现递归操作,此时生成器可以产生自身 。看下面的例子:
function* Fn(n) {
if (n > 0) {
yield* nTimes(n - 1);
yield n - 1;
}
}
for (const x of Fn(3)) {
console.log(x);
}
// 0
// 1
// 2
在这个例子中,每个生成器首先都会从新创建的生成器对象产出每个值,然后再产出一个整数 。结果就是生成器函数会递归地减少计数器值,并实例化另一个生成器对象 。从最顶层来看,这就相当于创建一个可迭代对象并返回递增的整数
三、提前终止生成器
与迭代器类似,生成器也支持“可关闭”的概念 。一个实现Iterator接口的对象一定有next()方法,还有一个可选的return()方法用于提前终止迭代器 。生成器对象除了有这两个方法,还有第三个方法:thorw() 。
通过return和 throw两种方法,提前终止生成器,都会强制生成器进入关闭状态(generatorFn {<closed>}) 。
return()
一旦进入关闭状态,之后再调用next()都会显示 done: true状态 。
function* gFn() {
yield 1;
yield 2;
yield 3;
}
const g = gFn();
console.log(g.next());
// {value: 1, done: false}
console.log(g.return("--return--"));
// {value: "--return--", done: true}
console.log(g);
// gFn {<closed>}
console.log(g.next(11));
// {value: undefined, done: true}
console.log(g.return(33));
// {value; 33, done; true}
当我们调用生成器的 return方法时,生成器会进入关闭状态,后续再调用 next方法,都会显示 done: true状态,且 value也只有在再次调用 return的时候才能得到不是 undefined的值 。
for-of循环等内置语言结构会忽略状态为done:true的IteratorObject内部返回的值 。
function* generatorFn() {
for (const x of [1, 2, 3]) {
yield x;
}
}
const g = generatorFn();
for (const x of g) {
console.log(x); // 1 2 3
}
for (const x of g) {
if (x > 1) {
g.return(4);
}
console.log(x); // 1 2
}
throw()
throw()方法会在暂停的时候将一个提供的错误注入到生成器对象中 。如果错误未被处理,生成器就会关闭 。
function* generatorFn() {
for (const x of [1, 2, 3]) {
yield x;
}
}
const g = generatorFn();
console.log(g); //generatorFn{<suspended>}
try {
g.throw("throw");
} catch (e) {
console.log(e); // throw
}
console.log(g); //generatorFn{<closed>}
不过,假如生成器函数内部处理了这个错误,那么生成器就不会关闭,而且还可以恢复执行 。错误处理会跳过对应的yield,因此在这个例子中会跳过一个值,比如:
推荐阅读
- 显示器中的 GSYNC 与 FreeSync 技术原理是什么
- Ajax是什么?JavaScript中如何使用Ajax技术进行网络请求?
- 一篇文章教会你使用JavaScript 创建对象
- 2023年流行的五大JavaScript框架
- 历代的“装饰玉器”:(4)玉镯【三】
- 我的电脑怎么没声音了
- 消失的她:全员演技过关,唯有一人是败笔,演技太烂像AI机器人
- 气鸣乐器包含哪些乐器、单簧气鸣乐器入门介绍
- 台式处理器天梯排行榜2022
- 公式编辑器怎么使用下标
