Iterator,Generator和Async/Await经常会被放在一起讨论,根据自己的理解做一个总结
一 Iterator
1 Iterator是遍历器,它是一种接口,作用如下:
- 为各种数据结构提供统一的接口访问;
- 数据结构的成员能够按某种次序排列;
- Iterator 接口主要供
for...of
消费
2 原生具备Iterator接口的数据结构如下:
Array
Map
Set
String
TypedArray
函数的 arguments 对象
NodeList 对象
…
它们有一个共同点,都具有Symbol.iterator
属性
3 Symbol.iterator
对象的Symbol.iterator
属性,指向该对象的默认遍历器方法;对象进行for...of
循环时,会调用Symbol.iterator
方法,返回该对象的默认遍历器.具体参照Symbol.iterator | MDN
为何必须实现它才可用for...of
遍历?这是一个约定,迭代协议 | MDN
let person = {}
person[Symbol.iterator] = function() {
let index = 1;
return {
next() {
return {done: index>10, value: index++}
}
}
}
for (const item of person) {
console.log(item)
}
4 场景
- 解构赋值
- 扩展运算符
- yield*
二 Generator
Generator是一个可以暂停和继续执行的函数,所以它是状态机,封装了多个内部状态;
function* helloWorldGenerator() {
yield 'hello';
yield 'world';
return 'ending';
}
var hw = helloWorldGenerator();
1 语法特点
function
关键字后面有一个星号- 函数内部用
yield
关键字返回值
2 与 Iterator 接口的关系
由于 Generator 函数就是遍历器生成函数,因此可以把 Generator 赋值给对象的Symbol.iterator
属性,从而使得该对象具有 Iterator
接口
function* gen(){
// some code
}
var g = gen();
g[Symbol.iterator]() === g
// true
3 注意事项
function* count() {
yield 1
yield 2
return 3
}
var c = count()
console.log(c.next()) // { value: 1, done: false }
console.log(c.next()) // { value: 2, done: false }
console.log(c.next()) // { value: 3, done: true }
console.log(c.next()) // { value: undefined, done: true }
function* count() {
yield 1
yield 2
return 3
}
var c = count()
for (let i of c){
console.log(i) // 1, 2
}
使用next()
执行可以拿到return后的值,但是如果是通过for...of
,将无法遍历到最后一个值;
另外for...of
本身也在触发next()
,当前的生成器都迭代完,for...of
将不再触发,手动执行next()也将只会返回 { value: undefined, done: true }
三 Async/Await
Generator 函数的语法糖,函数语法特点是将 Generator
函数的星号(*
)替换成async
,将yield
替换成await
1 特点
- 内置执行器:不需要调用
next()
方法,会自动执行函数内容,Generator如果想自动执行需要依赖co模块; - 语义清晰:
async
表示函数里有异步操作,await
表示紧跟在后面的表达式需要等待结果; - 适用性广泛:
co
模块约定yield
命令后面只能是 Thunk 函数(指将多参数函数,其中入参中包含了callback函数变成单参数版本的函数)或 Promise 对象,而async
函数的await
命令后面,可以是 Promise 对象和原始类型的值(数值,字符串和布尔值,但这时会自动转成立即 resolved 的 Promise 对象) - 返回值是 Promise
更多特点参照阮一峰的async 函数 - ECMAScript 6入门