在 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" ] });