ES9新增了异步迭代器(Async iterator),异步执行语句(for...await...of)和异步生成器(Async generator),本文带领大家了解这三个新特性,以及如何创建异步迭代器。
1. 迭代器(Iterator)
如果你还不了解ES6的
迭代器
,也就是iterator
,先来看看这一部分。
iterator
是一个特殊的对象,它包含一个next
方法,next
方法返回一个对象,这个对象包含两个属性,一个是value
,表示成员的值,一个是done
,done
的值是一个布尔类型
,表示迭代器
是否结束。
iterator.next() // 返回 {value: '', done: false}
复制代码
迭代器
内部会保存一个指针,指向迭代器的成员位置,每调用一次next
方法,指针就会移动到下一个成员,直到指针指向迭代器最后一个成员后面的位置,这时,done
的值为true
,value
的值一般为undefined
,需要根据iterator
的实际实现来决定。
1.1 创建iterator
实现一个函数,用来创建
iterator
。
几个关键点
iterator
是一个对象,并且含有一个next
方法next
方法返回一个对象,包含一个value
属性和done
属性,value
表示返回的值,done
是一个布尔类型,表示迭代器是否结束iterator
内部包含一个内部指针,指向迭代器的成员的位置,每调用一次next
方法,指针就会移动到下一个成员,直到指针指向迭代器最后一个成员后面的位置,这时done
的值为true
// 可以通过传入数组或者对象创建iterator
const createIterator = items => {
const keys = Object.keys(items)
const len = keys.length
let pointer = 0
return {
next() {
const done = pointer >= len
const value = !done ? items[keys[pointer++]] : undefined
return {
value,
done
}
}
}
}
复制代码
const iterator1 = createIterator([1, 2, 3])
iterator.next() // { value: 1, done: false }
iterator.next() // { value: 2, done: false }
iterator.next() // { value: 3, done: false }
iterator.next() // { value: undefined, done: true }
复制代码
const iterator2 = createIterator({a: 'a', b: 'b', c: 'c'})
iterator.next() // { value: 'a', done: false }
iterator.next() // { value: 'b', done: false }
iterator.next() // { value: 'c', done: false }
iterator.next() // { value: undefined, done: true }
复制代码
1.2 iterator和for...of
部署了iterator
接口的数据结构,也就是具有Symbol.iterator
方法的数据结构,就可以被for...of
遍历。Symbol.iterator
方法类似于上面实现的createIterator
函数
- 数组原生具有
iterator
接口
const arr = [1, 2, 3]
typeof arr[Symbol.iterator] // 'function'
for (const val of arr) {
console.log(val)
}
// 1
// 2
// 3
复制代码
- 对象默认没有
iterator
接口,但是我们可以自己部署
const obj = {a: 'a', b: 'b', c: 'c'}
typeof obj[Symbol.iterator] // 'undefined'
for (const val of obj) {
console.log(val)
}
// TypeError: obj is not iterable
复制代码
给对象部署iterator
接口
const obj = {a: 'a', b: 'b', c: 'c'}
obj[Symbol.iterator] = function() {
const self = this
const keys = Object.keys(self)
const len = keys.length
let pointer = 0
return {
next() {
const done = pointer >= len
const value = !done ? self[keys[pointer++]] : undefined
return {
value,
done
}
}
}
}
for (const val of obj) {
console.log(val)
}
// a
// b
// c
复制代码
2. 生成器(Generator)
Generator
是一个特殊的函数,函数体内部使用yield
表达式,定义不同的内部状态,当执行Generator
函数时,不会直接执行函数体,而是会返回一个遍历器对象(iterator)。
Generator
函数内部可以使用yield
表达式,定义内部状态function
关键字与函数名之间有一个*
function* generator() {
console.log('start');
yield 1
yield 2
yield 3
console.log('end')
}
const iterator = generator() // 这时函数体并没有被执行,而是创建了一个iterator
// 当调用iterator的next方法时,函数体开始执行,
iterator.next() // 'start' {value: 1, done: false}
iterator.next() // {value: 2, done: false}
iterator.next() // {value: 3, done: false}
iterator.next() // 'end' {value: undefined, done: true}
复制代码
- 每调用一次
next
方法,函数体会从函数头部或上次停下来的地方开始执行,直到遇到下一个yield
表达式或者return
语句时停止 yield
表达式后面的值会作为next
方法返回的对象的value
属性值return
会作为iterator
结束的标记,并且retur
n的值会作为next
方法返回的对象的value属性值
改写一下上面的例子
function* generator() {
yield 1
yield 2
return 3
}
const iterator = generator()
// 当调用iterator的next方法时,函数体开始执行,
iterator.next() // {value: 1, done: false}
iterator.next() // {value: 2, done: false}
iterator.next() // {value: 3, done: true}
复制代码
Generator
函数生成的iterator
可以被for...of
遍历
function* generator() {
yield 1
yield 2
yield 3
}
const iterator = generator()
typeof iterator[Symbol.iterator] // 'function'
for (const val of iterator) {
console.log(val)
}
// 1
// 2
// 3
复制代码
在这里我们只需要知道Generator
函数会生成一个iterator
就够了,但实际上Generator
函数远不止这些,这里我们不做详细介绍了,感兴趣的同学可以看看阮一峰Generator教程
3. 异步迭代器(Asynchronous Iterator)
ES9新增了异步迭代器
异步迭代器
和同步迭代器
相同,都是一个函数,并且含有一个next
方法,区别在于同步迭代器
的next
方法返回一个含有value
和done
属性的对象,而异步迭代器
的next
方法返回一个Promise
对象,并且Promise
对象的值为含有value
和done
属性的对象。
// 这是一个异步迭代器
asyncIterator.next().then(res => {
console.log(res.value, res.done)
})
复制代码
我们来实现一个创建异步迭代器
的方法
const createAsyncIterator = items => {
const keys = Object.keys(items)
const len = keys.length
let pointer = 0
return {
next() {
const done = pointer >= len
const value = !done ? items[keys[pointer++]] : undefined
return Promise.resolve({
value,
done
})
}
}
}
复制代码
和同步迭代器
相同,每调用一次next
方法,异步迭代器
内部的指针就移动到下一个成员
const aynscIterator = createAsyncIterator([1, 2, 3])
aynscIterator.next().then(({value, done}) => {
console.log(value, done) // 1 false
})
aynscIterator.next().then(({value, done}) => {
console.log(value, done) // 2 false
})
aynscIterator.next().then(({value, done}) => {
console.log(value, done) // 3 false
})
aynscIterator.next().then(({value, done}) => {
console.log(value, done) // undefined true
})
复制代码
3.1 for...await...of
for...of
方法能够遍历具有Symbol.iterator
接口的同步迭代器
数据,但是不能遍历异步迭代器
。 ES9新增的for...await...of
可以用来遍历具有Symbol.asyncIterator
方法的数据结构,也就是异步迭代器
,且会等待前一个成员的状态改变后才会遍历到下一个成员,相当于async
函数内部的await
。
定义一个具有Symbol.asyncIterator
方法的对象
const asyncItems = {
a: 1,
b: 2,
c: 3,
[Symbol.asyncIterator]() {
const items = this
const keys = Object.keys(items)
const len = keys.length
let pointer = 0
return {
next() {
const done = pointer >= len
const value = !done ? items[keys[pointer++]] : undefined;
return new Promise((resolve) => {
setTimeout(() => {
resolve({value, done})
}, 1000)
})
}
}
}
}
复制代码
使用for...await...of
遍历该对象
// await只能用在async函数中
async function run() {
for await (const value of asyncItems) {
console.log(value);
}
}
run();
// 1s后打印出 1
// 再过1s后打印出 2
// 再过1s后打印出 3
复制代码
上面的例子实现了每隔1s打印出对象的属性值的异步遍历器接口
,可以看到, 当使用for...await..of
,遍历时,会等待前一个Promise
对象的状态改变后,再遍历到下一个成员。
3.2 异步生成器(Async Generator)
我们可以采取一种更方便的方式创建异步迭代器
,就是利用异步生成器
。
异步生成器
和普通的生成器很像,但是其是async
函数,内部可以使用await
表达式,并且它返回一个具有Symbol.asyncIterator
方法的对象。
定义一个异步生成器
async function* asyncGenerator() {
yield await Promise.resolve(1);
yield await Promise.resolve(2);
yield await Promise.resolve(3);
}
复制代码
使用for...await...of
遍历该对象
const asyncIterator = asyncGenerator()
typeof asyncIterator[Symbol.asyncIterator] // 'function'
async function run() {
for await (const value of asyncIterator) {
console.log(value);
}
}
run();
// 1
// 2
// 3
复制代码
4. 总结
异步迭代器
与同步迭代器
相同的是,异步迭代器
也是一个具有next
方法的对象异步迭代器
对象的next
方法返回一个Promise对象
,Promise对象
的值为一个对象,包含一个value属性和一个done属性for...await...of
可以遍历具有Symbol.asyncIterator
方法的数据结构,并且会等待上一个成员状态改变后再继续执行异步生成器
(Async Generator)可以用来创建异步迭代器
,它是一个async
类型的generator
函数,内部可以使用await
表达式等待异步方法的执行完成,并使用for...await...of
遍历