ES9中的異步迭代器(Async iterator)和異步生成器(Async generator)

前方提醒: 篇幅較長,點個贊或者收藏一下,能夠在下一次閱讀時方便查找es6

ES9新增了異步迭代器(Async iterator)異步執行語句(for...await...of)異步生成器(Async generator),本文帶領你們瞭解這三個新特性,以及如何建立異步迭代器。數組

1. 迭代器(Iterator)

若是你還不瞭解ES6的迭代器,也就是iterator,先來看看這一部分。數據結構

iterator是一個特殊的對象,它包含一個next方法,next方法返回一個對象,這個對象包含兩個屬性,一個是value,表示成員的值,一個是donedone的值是一個布爾類型,表示迭代器是否結束。異步

iterator.next() // 返回 {value: '', done: false}
複製代碼

迭代器內部會保存一個指針,指向迭代器的成員位置,每調用一次next方法,指針就會移動到下一個成員,直到指針指向迭代器最後一個成員後面的位置,這時,done的值爲truevalue的值通常爲undefined,須要根據iterator的實際實現來決定。async

1.1 建立iterator

實現一個函數,用來建立iterator函數

幾個關鍵點ui

  • 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函數this

  • 數組原生具備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接口spa

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結束的標記,而且return的值會做爲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方法返回一個含有valuedone屬性的對象,而異步迭代器next方法返回一個Promise對象,而且Promise對象的值爲含有valuedone屬性的對象。

// 這是一個異步迭代器
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遍歷
相關文章
相關標籤/搜索