前言
在JS中,我们常常需要使用async以及await语法来发送网络请求等操作,但是这个语法的背后实现原理到底是什么呢?,我们就得需要了解迭代器(iterator)、可迭代对象、生成器(generator)这些知识点也在各种开源库中,广泛应用。
什么是迭代器(iterator)
在JS中,迭代器是一个对象,这个对象中有一个next()方法,每次调用会retrun出一个对象,这个对象中有两个参数,done(bool类型)、value(具体的属性值),当迭代未完全完成时,done的值则会一直为false,当value的值为最后一个时,下一次的next()调用返回的done则为true,表明迭代完全完成,此时的value值为undefined。
下面则是一个简单的迭代器实现
const obj=['1','i','n']
const iterator=(obj)=>{
let index=0
return {
next(){
if(index<obj.length){
return {done:false,value:obj[index++]}
}
else{
return {done:true,value:undefined}
}
}
}
}
const iteratorObj=iterator(obj)
console.log(iteratorObj.next())
console.log(iteratorObj.next())
console.log(iteratorObj.next())
console.log(iteratorObj.next())
调用四次next()方法输出结果如下
每一次调用next()方法就会返回迭代结果。
什么是可迭代对象
一种很简单的说法就是,具有迭代器的对象就是可迭代对象,但是这个迭代器的名称为[Symbol.iterator], 它和迭代器是不同的概念,当一个对象实现了iterable protocol协议时,他就是一个可迭代对象,这个对象的要求是必须实现@@iterator方法,在代码中我们使用Symbol.iterator访问该属性。
以下为简单的可迭代对象代码实现
const iteratorObj={
obj:['1','i','n'],
[Symbol.iterator]:function (){
let index=0
return{
next:()=>{
if(index<this.obj.length){
return {done:false,value:this.obj[index++]}
}
else{
return {done:true,value:undefined}
}
}
}
}
}
那么可迭代对象有什么用呢?,我们都知道,for of 遍历的对象都需要是有迭代器的对象,比如Set、Map、Array,所以我们简单实现的这个可迭代对象是可以用 for of 遍历的。
所以我们用for of 遍历输出一些结果,结果如下
在这里,我们的next()方法一定要用箭头函数,将this指针绑定为interatorObj
可迭代对象的应用场景
- Javascript语法中,for of、展开语法(spread syntax)、yield*(yield*返回的必须是一个可迭代对象)、数组的解构赋值。
- 创建一些对象时:new Map([Iterable])、new WeakMap([Iterable])、new Set([Iterable])、new WeakSet([Iterable])。
- 一些方法的调用:Promise.all(Iterable)、Promise.race(Iterable)、Array.from(Iterable)。
注意!!!
const newObj={...obj}
像这个展开语法,没有用到可迭代对象,而是在ES9(ES2018)中新增的一个特性。
const {done,value}=obj
这种对象的解构赋值也是ES9新增的特性
什么是生成器
生成器是ES6中新增的一种函数控制、使用的方案、它可以让我们更加灵活的控制函数什么时候继续执行、暂停执行等。
平时我们会编写很多的函数,这些函数终止的条件通常是返回值或者发生了异常。
在我们了解生成器之前,我们需要了解生成器函数。
生成器函数
生成器函数也是一个函数,但是和普通的函数有一些区别
- 生成器函数需要在function的后面加一个符号:*。
- 生成器函数可以通过yield关键字来控制函数的执行流程。
- 生成器函数的返回值是一个Generator(生成器)。
生成器事实上是一种特殊的迭代器。 MDN:Instead,they return a special type of iterator,called a Generator
下面是一个简单的生成器函数代码
function* foo(){
const value1='1'
console.log(value1)
yield
const value2='i'
console.log(value2)
yield
const value3='n'
console.log(value3)
yield
}
const generator=foo()
generator.next()
generator.next()
generator.next()
执行结果如下
生成器函数通过yield关键字控制函数的执行,当我们每一次调用next()方法时,就会调用yield对应顺序的以下代码。
yield关键字传出参数
当然,我们也可以通过在yield后边跟参数,将参数传出,并在next()方法调用时接收
function* foo(){
const value1='1'
console.log(value1)
yield 'export 1'
const value2='i'
console.log(value2)
yield 'export i'
const value3='n'
console.log(value3)
yield 'export n'
}
const generator=foo()
const word1=generator.next()
const word2=generator.next()
const word3=generator.next()
console.log(word1)
console.log(word2)
console.log(word3)
输出结果如下
next()方法调用传入参数
当然,我们也可以在next()方法调用时,传入参数,并用每一次yield执行时接收参数。
function* foo(){
const value1='1'
console.log(value1)
const s1=yield 'export 1'
console.log(s1)
const value2='i'
console.log(value2)
const s2=yield 'export i'
console.log(s2)
const value3='n'
console.log(value3)
const s3= yield 'export n'
console.log(s3)
}
const generator=foo()
const word1=generator.next()
const word2=generator.next('incoming 1')
const word3=generator.next('incoming i')
const word4=generator.next('incoming n')
console.log(word1)
console.log(word2)
console.log(word3)
console.log(word4)
执行结果如下
return方法以及throw方法
通过调用return()方法可以直接return出函数,不会再执行后边的函数,也无法用yield控制函数执行的过程,next()方法也无法执行,throw方法则可以抛出异常。通过try catch方法去捕获错误。
yield*
在生成器函数中 yield可以传出参数,但是当我们用yield* 传出参数时,传出的参数必须是可迭代对象,async和await语法糖也是用到了yield*实现。
总结
- 迭代器是一个对象,这个对象中有一个next()方法,每次调用会retrun出一个对象,这个对象中有两个参数,done(bool类型)、value(具体的属性值)
- 当一个对象实现了iterable protocol协议时,他就是一个可迭代对象,这个对象的要求是必须实现@@iterator方法,在代码中我们使用Symbol.iterator访问该属性。
- 生成器是ES6中新增的一种函数控制、使用的方案、它可以让我们更加灵活的控制函数什么时候继续执行、暂停执行等。
- 生成器事实上是一种特殊的迭代器。
- yield关键字传出参数、next()方法调用传入参数。