ES9中的非同步迭代器(Async iterator)和非同步生成器(Async generator)
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
遍歷