什么是同步迭代器呢?
舉個例子:
var obj = {
*[Symbol.iterator]() {
yield 1;
yield 2;
yield 3;
}
}
var iterator = obj[Symbol.iterator]()
iterator.next() // {value: 1, done: false}
iterator.next() // {value: 2, done: false}
iterator.next() // {value: 3, done: false}
iterator.next() // {value: undefined, done: true}
這里的 iterator 就是同步迭代器了,每調(diào)用一次 next 方法,就返回一個 { value: xx, done: xx } 形式的對象。
什么是異步迭代器呢?
再舉個例子:
var obj = {
async *[Symbol.asyncIterator]() {
yield 1;
yield 2;
yield 3;
}
}
var asyncIterator = obj[Symbol.asyncIterator]()
asyncIterator.next().then(data => console.log(data)) // {value: 1, done: false}
asyncIterator.next().then(data => console.log(data)) // {value: 2, done: false}
asyncIterator.next().then(data => console.log(data)) // {value: 3, done: false}
asyncIterator.next().then(data => console.log(data)) // {value: undefined, done: true}
與同步可迭代對象部署了 [Symbol.iterator] 屬性不同的是,異步可迭代對象的標(biāo)志是部署了 [Symbol.asyncIterator] 這個屬性。
這里的 asyncIterator 就是異步迭代器了。與同步迭代器 iterator 不同的是,在 asyncIterator 上調(diào)用 next 方法得到是一個 Promise 對象,其內(nèi)部值是 { value: xx, done: xx } 的形式,類似于 Promise.resolve({ value: xx, done: xx })。
為什么會有異步迭代器呢?
同步迭代器里數(shù)據(jù)都是當(dāng)時就能獲取的(沒有延遲),而異步迭代器里的數(shù)據(jù)往往獲取是需要時間的(有延遲)。
下面舉了幾個例子,來說明為什么要用異步迭代器,以及它的使用場景。
- 同步迭代器數(shù)據(jù)即用即給,處理順序等于遍歷順序。
var obj = {
*[Symbol.iterator]() {
yield 1;
yield 2;
yield 3;
}
}
for (let item of obj) {
console.log(item) // 1 -> 2 -> 3。處理順序等于遍歷順序
}
- 如果同步迭代器數(shù)據(jù)獲取需要時間,那么再用
for-of遍歷的話,就有問題。
var obj = {
*[Symbol.iterator]() {
yield new Promise(resolve => setTimeout(() => resolve(1), 5000));
yield new Promise(resolve => setTimeout(() => resolve(2), 2000));
yield new Promise(resolve => setTimeout(() => resolve(3), 500));
}
}
console.log(Date.now())
for (let item of obj) {
item.then(data => console.log(Date.now(), data))
}
// 1579253648926
// 1579253649427 3 // 1579253649427 - 1579253648926 = 501
// 1579253650927 2 // 1579253650927 - 1579253648926 = 2001
// 1579253653927 1 // 1579253653927 - 1579253648926 = 5001
可以把這里的每個 item 當(dāng)成是接口請求,數(shù)據(jù)返回的時間不一定的。上面的打印結(jié)果就說明了問題所在——我們控制不了數(shù)據(jù)的處理順序。
- 再來看看異步迭代器
var obj = {
async *[Symbol.asyncIterator]() {
yield new Promise(resolve => setTimeout(() => resolve(1), 5000));
yield new Promise(resolve => setTimeout(() => resolve(2), 3000));
yield new Promise(resolve => setTimeout(() => resolve(3), 500));
}
}
console.log(Date.now())
for await (let item of obj) {
console.log(Date.now(), item)
}
// 1579256590699
// 1579256595700 1 // 1579256595700 - 1579256590699 = 5001
// 1579256598702 2 // 1579256598702 - 1579256590699 = 8003
// 1579256599203 3 // 1579256599203 - 1579256590699 = 8504
注意,異步迭代器要聲明在 [Symbol.asyncIterator] 屬性里,使用 for-await-of 循環(huán)處理的。最終效果是,對任務(wù)挨個處理,上一個任務(wù)等待處理完畢后,再進(jìn)入下一個任務(wù)。
因此,異步迭代器就是用來處理這種不能即時拿到數(shù)據(jù)的情況,還能保證最終的處理順序等于遍歷順序,不過需要依次排隊等待。
for-await-of
for-await-of 除了能用在異步可迭代對象上,還能用在同步可迭代對象上。
var obj = {
*[Symbol.iterator]() {
yield 1;
yield 2;
yield 3;
}
}
for await(let item of obj) {
console.log(item) // 1 -> 2 -> 3
}
是這樣的,如果 for-await-of 發(fā)現(xiàn)遍歷對象上沒有 [Symbol.asyncIterator] 的話,就去調(diào)用 [Symbol.iterator]。我們都知道,由此得來的是同步迭代器,在這個迭代器上調(diào)用 next 方法的時候,返回就是一個普通的對象,結(jié)構(gòu)類似于 { value: xx, done: xx }。
而 await obj 的結(jié)果還是 obj,下例為證:
(async function() {
var obj = { value: 1, done: false }
console.log((await obj) === obj) // true
})()
所以才會有上面的結(jié)果。
還有一點,需要注意的是,如果一個對象上同時部署了 [Symbol.asyncIterator] 和 [Symbol.iterator],那就會優(yōu)先使用 [Symbol.asyncIterator] 生成的異步迭代器。這很好理解,因為 for-await-of 本來就是為異步迭代器而生的。
var obj = {
*[Symbol.iterator]() {
yield 1;
yield 2;
yield 3;
},
async *[Symbol.asyncIterator]() {
yield 4;
yield 5;
yield 6;
}
}
for await(let item of obj) {
console.log(item) // 4 -> 5 -> 6。優(yōu)先使用由 [Symbol.asyncIterator] 生成的異步迭代器
}