在 ECMAScript 2015(ES6) 中 JavaScript 引入了迭代器接口(iterator)用來遍歷數據。迭代器對象知道如何每次訪問集合中的一項, 並跟蹤該序列中的當前位置。在 JavaScript 中迭代器是一個對象,它提供了一個 next()
方法,用來返回序列中的下一項。這個方法返回包含兩個屬性:done
和 value
。html
迭代器對象一旦被建立,就能夠反覆調用 next()
。java
function makeIterator(array) { let nextIndex = 0; // 初始索引 // 返回一個迭代器對象,對象的屬性是一個 next 方法 return { next: function() { if (nextIndex < array.length) { // 當沒有到達末尾時,返回當前值,並把索引加1 return { value: array[nextIndex++], done: false }; } // 到達末尾,done 屬性爲 true return {done: true}; } }; }
一旦初始化,next()
方法能夠用來依次訪問對象中的鍵值:git
const it = makeIterator(['j', 'u', 's', 't']); it.next().value; // j it.next().value; // u it.next().value; // s it.next().value; // t it.next().value; // undefined it.next().done; // true it.next().value; // undefined
一個定義了迭代行爲的對象,好比在 for...of
中循環了哪些值。爲了實現可迭代,一個對象必須實現 @@iterator
方法,這意味着這個對象(或其原型鏈中的一個對象)必須具備帶 Symbol.iterator
鍵的屬性:程序員
String
,Array
,TypedArray
,Map
和 Set
都內置可迭代對象,由於它們的原型對象都有一個 Symbol.iterator
方法。github
const justjavac = { [Symbol.iterator]: () => { const items = [`j`, `u`, `s`, `t`, `j`, `a`, `v`, `a`, `c`]; return { next: () => ({ done: items.length === 0, value: items.shift() }) } } }
當咱們定義了可迭代對象後,就能夠在 Array.from
、for...of
中使用這個對象:正則表達式
[...justjavac]; // ["j", "u", "s", "t", "j", "a", "v", "a", "c"] Array.from(justjavac) // ["j", "u", "s", "t", "j", "a", "v", "a", "c"] new Set(justjavac); // {"j", "u", "s", "t", "a", "v", "c"} for (const item of justjavac) { console.log(item) } // j // u // s // t // j // a // v // a // c
因爲在迭代器方法返回時,序列中的下一個值和數據源的 "done" 狀態必須已知,因此迭代器只適合於表示同步數據源。chrome
雖然 JavaScript 程序員遇到的許多數據源是同步的(好比內存中的列表和其餘數據結構),可是其餘許多數據源卻不是。例如,任何須要 I/O 訪問的數據源一般都會使用基於事件的或流式異步 API 來表示。不幸的是,迭代器不能用來表示這樣的數據源。api
(即便是 promise 的迭代器也是不夠的,由於它的 value 是異步的,可是迭代器須要同步肯定 "done" 狀態。)promise
爲了給異步數據源提供通用的數據訪問協議,咱們引入了 AsyncIterator
接口,異步迭代語句(for-await-of
)和異步生成器函數。babel
一個異步迭代器就像一個迭代器,除了它的 next()
方法返回一個 { value, done }
的 promise。如上所述,咱們必須返回迭代器結果的 promise,由於在迭代器方法返回時,迭代器的下一個值和「完成」狀態可能未知。
咱們修改一下以前的代碼:
const justjavac = { - [Symbol.iterator]: () => { + [Symbol.asyncIterator]: () => { const items = [`j`, `u`, `s`, `t`, `j`, `a`, `v`, `a`, `c`]; return { - next: () => ({ + next: () => Promise.resolve({ done: items.length === 0, value: items.shift() }) } } }
好的,咱們如今有了一個異步迭代器,代碼以下:
const justjavac = { [Symbol.asyncIterator]: () => { const items = [`j`, `u`, `s`, `t`, `j`, `a`, `v`, `a`, `c`]; return { next: () => Promise.resolve({ done: items.length === 0, value: items.shift() }) } } }
咱們可使用以下代碼進行遍歷:
for await (const item of justjavac) { console.log(item) }
若是你遇到了 SyntaxError: for await (... of ...) is only valid in async functions and async generators
錯誤,那是由於 for-await-of
只能在 async 函數或者 async 生成器裏面使用。
修改一下:
(async function(){ for await (const item of justjavac) { console.log(item) } })();
// 迭代器 interface Iterator { next(value) : IteratorResult; [optional] throw(value) : IteratorResult; [optional] return(value) : IteratorResult; } // 迭代結果 interface IteratorResult { value : any; done : bool; }
// 異步迭代器 interface AsyncIterator { next(value) : Promise<IteratorResult>; [optional] throw(value) : Promise<IteratorResult>; [optional] return(value) : Promise<IteratorResult>; } // 迭代結果 interface IteratorResult { value : any; done : bool; }
異步生成器函數與生成器函數相似,但有如下區別:
next
,throw
,和return
),每一個方法都返回一個 Promise,Promise 返回 { value, done }
。而普通生成器函數並不返回 Promise,而是直接返回 { value, done }
。這會自動使返回的異步生成器對象具備異步迭代的功能。await
表達式和 for-await-of
語句。yield*
的行爲以支持異步迭代。示例:
async function* readLines(path) { let file = await fileOpen(path); try { while (!file.EOF) { yield await file.readLine(); } } finally { await file.close(); } }
函數返回一個異步生成器(async generator)對象,能夠用在 for-await-of
語句中。
Facebook 的 Regenerator 項目爲 AsyncIterator
接口提供了一個 polyfill,將異步生成器函數變成返回 AsyncIterator
的對象 ECMAScript 5 函數。Regenerator 還不支持 for-await-of
異步迭代語法。
Babylon parser 項目支持異步生成器函數和 for- await-of
語句(v6.8.0+)。你可使用它的 asyncGenerators 插件。
require("babylon").parse("code", { sourceType: "module", plugins: [ "asyncGenerators" ] });
另外,從 6.16.0 開始,異步迭代被包含在 Babel 的 "babel-plugin-transform-async-generator-functions"
下以及 babel-preset-stage-3
。
require("babel-core").transform("code", { plugins: [ "transform-async-generator-functions" ] });