ES2018 新特征
- 异步迭代器(本文)
- 正则表达式反向(lookbehind)断言
- 正则表达式 Unicode 转义
- 非转义序列的模板字符串
- 正则表达式 s/dotAll 形式
- 正则表达式定名捕捉组
- 对象睁开运算符
- Promise.prototype.finally
1. 概述
在 ECMAScript 2015(ES6) 中 JavaScript 引入了迭代器接口(iterator)用来遍历数据。迭代器对象晓得怎样每次接见鸠合中的一项, 并跟踪该序列中的当前位置。在 JavaScript 中迭代器是一个对象,它供应了一个 next()
要领,用来返回序列中的下一项。这个要领返回包括两个属性:done
和 value
。
迭代器对象一旦被建立,就能够重复挪用 next()
。
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()
要领能够用来顺次接见对象中的键值:
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
2. 可迭代对象
一个定义了迭代行动的对象,比方在 for...of
中轮回了哪些值。为了完成可迭代,一个对象必需完成 @@iterator
要领,这意味着这个对象(或其原型链中的一个对象)必需具有带 Symbol.iterator
键的属性:
String
,Array
,TypedArray
,Map
和 Set
都内置可迭代对象,因为它们的原型对象都有一个 Symbol.iterator
要领。
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
3. 同步迭代
因为在迭代器要领返回时,序列中的下一个值和数据源的 “done” 状况必需已知,所以迭代器只适合于示意同步数据源。
虽然 JavaScript 程序员碰到的很多数据源是同步的(比方内存中的列表和其他数据结构),然则其他很多数据源却不是。比方,任何须要 I/O 接见的数据源一般都邑运用基于事宜的或流式异步 API 来示意。不幸的是,迭代器不能用来示意如许的数据源。
(即使是 promise 的迭代器也是不够的,因为它的 value 是异步的,然则迭代器须要同步肯定 “done” 状况。)
为了给异步数据源供应通用的数据接见协定,我们引入了 AsyncIterator
接口,异步迭代语句(for-await-of
)和异步生成器函数。
4. 异步迭代器
一个异步迭代器就像一个迭代器,除了它的 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)
}
})();
5. 同步迭代器 vs 异步迭代器
5.1 Iterators
// 迭代器
interface Iterator {
next(value) : IteratorResult;
[optional] throw(value) : IteratorResult;
[optional] return(value) : IteratorResult;
}
// 迭代效果
interface IteratorResult {
value : any;
done : bool;
}
5.2 Async Iterators
// 异步迭代器
interface AsyncIterator {
next(value) : Promise<IteratorResult>;
[optional] throw(value) : Promise<IteratorResult>;
[optional] return(value) : Promise<IteratorResult>;
}
// 迭代效果
interface IteratorResult {
value : any;
done : bool;
}
6. 异步生成器函数
异步生成器函数与生成器函数相似,但有以下区分:
- 当被挪用时,异步生成器函数返回一个对象,”async generator”,含有 3 个要领(
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
语句中。
7. 完成
- Chakra – 暂未支撑
- JavaScriptCore – Safari Tech Preview 40
- SpiderMonkey – Firefox 57
- V8 – Chrome 63
Polyfills
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"
]
});