《红宝书》 |生成器
生成器拥有在一个函数块内暂停和恢复代码执行的能力
生成器函数
生成器的形式是一个函数,而函数名前面加上一个*
表示它是一个生成器。只要是可以定义函数的地方,就能定义生成器:
//生成器函数声明
function * generatorFn(){}
//生成器函数表达式
let generatorFn=function * (){}
//作为对象字面量方法的生成器函数
let foo={
* generatorFn(){}
}
//作为类实例方法的生成器函数
class Foo{
* generatorFn(){}
}
//作为类静态方法的生成器函数
class Foo{
static * generatorFn(){}
}
生成器的
*
不受两侧空格影响,箭头函数不能用来定义生成器函数。
生成器对象
调用生成器函数会产生一个生成器对象:
//生成器函数
function * generatorFn(){}
//生成器对象
let g=generatorFn()
生成器对象实现了iterator
接口,即生成器对象也可称为是迭代器对象。生成器一开始是处于暂停执行(suspended)状态的,调用next()
方法能让其开始或恢复执行。
next()
的返回值类似于迭代器,具有done
和value
属性。函数体为空的生成器函数调用一次next()
就会让生成器对象到达{done:true}
状态:
function * generatorFn(){}
let g=generatorFn()
console.log(g[Symbol.iterator]) //ƒ [Symbol.iterator]() { [native code] } --表明实现了iterator接口
console.log(g[Symbol.iterator]()) //generatorFn {<suspended>} --返回一个生成器,打印值表示一开始是暂停执行
console.log(g) //generatorFn {<suspended>} --表示一开始是暂停执行
console.log(g.next) //f next() { [native code] } --表示生成器对象具备next方法
console.log(g.next()) //{value: undefined, done: true} --调用next方法恢复执行
yield基本概念
yield关键字可以让生成器停止和开始执行。生成器函数在遇到之后执行会停止,函数作用域的状态会被保留,需要在生成器对象上调用next()方法恢复执行:
function * generatorFn(){
yield
}
let g=generatorFn()
console.log(g.next()) //{value: undefined, done: false} --遇到了yield停止执行
console.log(g.next()) //{value: undefined, done: true} --接着触发next,恢复执行
通过next()
返回的对象中,value
属性表示生成器函数的返回值,默认值为undefined
。我们可通过yield
或return
指定生成器函数的返回值,不同的点在于,yield
返回的对象处于done:false
状态,而return
返回的对象处在done:true
状态:
function * generatorFn(){
yield "one";
yield "two"
return "three"
}
let g=generatorFn()
console.log(g.next()) //{value: "one", done: false}
console.log(g.next()) //{value: "two", done: false}
console.log(g.next()) //{value: "three", done: true}
在生成器对象上调用next()是互不干扰的:
function * generatorFn(){
yield "one";
yield "two"
return "three"
}
let g1=generatorFn()
let g2=generatorFn()
console.log(g1.next()) //{value: "one", done: false}
console.log(g2.next()) //{value: "one", done: false}
console.log(g2.next()) //{value: "two", done: false}
console.log(g1.next()) //{value: "two", done: false}
yield关键字只能在生成器函数内部调用,否则抛错:
//无效
function * generatorFn(){
function fnname(){
yield;
}
}
//无效
function * generatorFn(){
const fnname=()=>{
yield;
}
}
yield应用
可迭代对象
生成器对象显式调用next()的用处不大,把它当做可迭代对象用起来更方便:
//无效
function * generatorFn(){
yield "one";
yield "two"
yield "three"
}
//这里的generatorFn()就相当于一个生成器对象,因为调用了generatorFn生成器函数
for( const item of generatorFn()){
console.log(item)
}
自定义迭代对象
生成一个迭代器,执行指定的次数:
//自定义可迭代对象:生成n个yield
function * nTime(n){
//当n减为0时,相当于flase,退出循环
while(n--){
yield;
}
}
//创建迭代器:nTime(3)
for(let n of nTime(3)){
console.log('hello')
}
实现范围和填充数组
function * range(start,end){
while(end>start){
yield start++
}
}
for(const x of range(4,7)){
console.log(x)
}
实现输入输出
yield会接受传给next()的值:
function * state(initial){
console.log(initial)
console.log(yield)
console.log(yield)
}
let g=state('hello')
console.log(g.next('one')) //hello --遇到yield停止执行,所以返回hello
console.log(g.next('two')) //two --再次调用next恢复执行,yield成功接受值并打印
console.log(g.next('three')) //three
产生可迭代对象
可以使用yield*
让它能够迭代一个可迭代对象,从而一次产出一个值(星号两侧的值不影响其行为):
function * generatorFn(){
yield* [1,2,3]
yield* [4,5]
}
let g=generatorFn()
for(const x of g){
console.log(x)
}
//1
//2
//3
//4
//5
生成器作为默认迭代器
生成器函数和默认迭代器(Symbol.iterator
)被调用后都会产生迭代器,所以生成器函数适合作为默认迭代器:
class Foo{
constructor(){
this.value=[1,2,3]
}
*[Symbol.iterator](){
yield* this.value
}
}
const f=new Foo()
for (const x of f){
console.log(x)
}
提前终止生成器
可用过return()
或throw()
提前终止生成器:
-
return()
会强制生成器进入关闭状态,提供给return 的值就是终止迭代器对象的值function * generatorFn(){ yiled* [1,2,3] } let g=generatorFn() console.log(g) //generatorFn {<suspended>} console.log(g.return(4)) //{value: 4, done: true} console.log(g) //generatorFn {<closed>} console.log(g.next()) //{value: undefined, done: true} --只要进入关闭状态,后续调用next都返回done:done
-
throw()
会在暂停的时候将一个提供的错误注入到生成器对象中,如果错误未被处理,生成器就会关闭:function * generatorFn(){ yiled* [1,2,3] } let g=generatorFn() console.log(g) //generatorFn {<suspended>} try{ g.throw('foo') }catch(e){ console.log(e) //foo } console.log(g) //generatorFn {<closed>}
如果生成器函数内部处理了这个错误,生成器就不会关闭。错误处理会跳过对应的yiled:
function * generatorFn(){ for(const x of [1,2,3]){ try{ yield x; }catch(e){ console.log('error:'e) } } } let g=generatorFn() console.log(g.next()) //{value: 1, done: false} g.throw('foo') //error:foo console.log(g.next()) //{value: 3, done: false}
相关文章:生成器函数的异步应用