JavaScript 交互式扩展(IxJS)
IxJS 是一组库,用于在 JavaScript 中组合同步和异步集合,以及 Array#extras 风格的组合
JavaScript 交互式扩展(IxJS)将 Array#extras 组合器引入到可迭代对象、生成器、异步可迭代对象和异步生成器中。随着 ES2015 引入 Symbol.iterator
和生成器,以及后续引入 Symbol.asyncIterator
和异步生成器,我们显然需要一个抽象来处理这些数据结构的组合、查询等操作。
IxJS 统一了同步和异步的基于拉取的集合,就像 RxJS 统一了基于推送的集合世界一样。RxJS 非常适合事件驱动的工作流,其中数据可以按生产者的速率推送,而 IxJS 则非常适合 I/O 操作,您作为消费者可以在准备好时拉取数据。
从 npm 安装 IxJS
npm install ix
(另请阅读我们如何打包 IxJS)
Iterable
Iterable
类提供了一种创建和组合同步集合的方式,类似于 JavaScript 中的数组、映射和集合,使用您熟悉的 Array#extras 风格方法,如 map
、filter
、reduce
等。我们可以使用 for ... of
语句来遍历我们的集合。
// ES
import { from } from 'ix/iterable';
import { filter, map } from 'ix/iterable/operators';
// CommonJS
const from = require('ix/iterable').from;
const { filter, map } = require('ix/iterable/operators');
const source = function* () {
yield 1;
yield 2;
yield 3;
yield 4;
};
const results = from(source()).pipe(
filter(x => x % 2 === 0),
map(x => x * x)
);
for (let item of results) {
console.log(`Next: ${item}`);
}
// Next 4
// Next 16
此外,我们还提供了 forEach
方法,由您选择使用哪一个。
// ES
import { from } from 'ix/asynciterable';
import { filter, map } from 'ix/asynciterable/operators';
// CommonJS
const from = require('ix/asynciterable').from;
const { filter, map } = require('ix/asynciterable/operators');
const source = function* () {
yield 1;
yield 2;
yield 3;
yield 4;
};
const results = from(source()).pipe(
filter(x => x % 2 === 0),
map(x => x * x)
);
results
.forEach(item => {
console.log(`Next: ${item}`);
});
// Next 4
// Next 16
为了打包考虑,我们可以不引入整个 Iterable
库,而是选择我们需要的运算符,直接添加到 Iterable
原型上。
// ES
import { IterableX as Iterable } from 'ix/iterable';
import 'ix/add/iterable/of';
import 'ix/add/iterable-operators/map';
// CommonJS
const { IterableX: Iterable } = require('ix/iterable');
require('ix/add/iterable/of');
require('ix/add/iterable-operators/map');
const results = Iterable.of(1,2,3)
.map(x => x + '!!');
Iterable
对象通过暴露 [Symbol.iterator]
方法来实现 JavaScript 中的迭代器模式,该方法返回 Iterator
类。迭代器通过调用 next()
方法来产生值,该方法返回 IteratorResult
类。
interface Iterable<T> {
[Symbol.iterator](): Iterator<T>;
}
interface Iterator<T> {
next(value?: any): IteratorResult<T>;
return?(value?: any): IteratorResult<T>;
throw?(e?: any): IteratorResult<T>;
}
interface IteratorResult<T> {
value: T;
done: Boolean;
}
AsyncIterable
AsyncIterable
对象基于 ECMAScript 提案中的异步迭代器。这允许我们创建 Promise 的异步集合,并能够使用我们可以导入的 map
、filter
、reduce
等方法。与 Iterable
对象类似,我们可以使用 for await ... of
来遍历异步集合。
// ES
import { from } from 'ix/asynciterable';
import { filter, map } from 'ix/asynciterable/operators';
// CommonJS
const from = require('ix/asynciterable').from;
const { filter, map } = require('ix/asynciterable/operators');
const source = async function* () {
yield 1;
yield 2;
yield 3;
yield 4;
};
const results = from(source()).pipe(
filter(async x => x % 2 === 0),
map(async x => x * x)
);
for await (let item of results) {
console.log(`Next: ${item}`);
}
// Next 4
// Next 16
另外,我们可以使用内置的 forEach
方法,如果有任何错误,可以使用 catch
捕获:
// ES
import { from } from 'ix/asynciterable';
import { filter, map } from 'ix/asynciterable/operators';
// CommonJS
const from = require('ix/asynciterable').from;
const { filter, map } = require('ix/asynciterable/operators');
const source = async function* () {
yield 1;
yield 2;
yield 3;
yield 4;
};
const results = from(source()).pipe(
filter(async x => x % 2 === 0),
map(async x => x * x)
);
results
.forEach(item => {
console.log(`Next: ${item}`);
})
.catch(err => {
console.log(`Error ${err}`);
});
for await (let item of results) {
console.log(`Next: ${item}`);
}
// Next 4
// Next 16
为了打包考虑,我们可以不引入整个 AsyncIterable
库,而是选择我们需要的运算符,直接添加到 AsyncIterable
原型上。
// ES
import { AsyncIterableX as AsyncIterable } from 'ix/asynciterable';
import 'ix/add/async-iterable/of';
import 'ix/add/asynciterable-operators/map';
// CommonJS
const { AsyncIterableX: AsyncIterable } = require('ix/asynciterable');
require('ix/add/asynciterable-operators/map');
const results = AsyncIterable.of(1,2,3)
.map(x => x + '!!');
AsyncIterable
类通过暴露 [Symbol.asyncIterator]
方法来实现 JavaScript 中的异步迭代器模式,该方法进而暴露 AsyncIterator
类。迭代器通过调用 next()
方法产生值,该方法返回一个 Promise,解析为 IteratorResult
类。
interface AsyncIterable<T> {
[Symbol.asyncIterator](): AsyncIterator<T>;
}
interface AsyncIterator<T> {
[Symbol.asyncIterator](): AsyncIterator<T>;
next(value?: any): Promise<IteratorResult<T>>;
return?(value?: any): Promise<IteratorResult<T>>;
throw?(e?: any): Promise<IteratorResult<T>>;
}
interface IteratorResult<T> {
value: T;
done: Boolean;
}
从 Iterable 转换为 AsyncIterable
使用 IxJS,你可以通过多种方法轻松地将 Iterable
转换为 AsyncIterable
。首先,我们可以使用 from
函数,既可以作为独立函数使用,也可以在 Ix.AsyncIterable
对象上使用。from
方法接受标准的 Iterable
、Generator
和 Promise 的 Iterator
,甚至是另一个 AsyncIterable
。
import { from } from 'ix/asynciterable';
import { map } from 'ix/asynciterable/operators';
const xs = [1, 2, 3, 4];
const mapped = from(xs).pipe(
map(async (item, index) => item * index)
);
for await (let item of mapped) {
console.log(`Next: ${item}`);
}
// Next 0
// Next 2
// Next 6
// Next 12
贡献
我们感谢对 IxJS 项目的贡献。IxJS 项目的发展得益于像您这样的社区参与。请阅读以下内容,了解如何参与。
行为准则
IxJS 项目有一个严格的行为准则,必须始终遵守。这个行为准则来自贡献者公约。请阅读完整文本,了解哪些行为是允许的,哪些是不允许的。
贡献指南
阅读贡献指南,了解如何参与 IxJS 项目。这包括我们的开发流程以及如何在提交代码之前进行测试。
打包
IxJS
用 TypeScript 编写,但项目被编译为多个 JS 版本和常见的模块格式。为了方便起见,基本的 IxJS 包包含了所有的编译目标,但如果你对 node_modules 的体积很敏感,别担心 —— 我们也考虑到了这一点。这些目标也以 @reactivex 命名空间发布:
npm install @reactivex/ix-ts # TypeScript 目标
npm install @reactivex/ix-es5-cjs # ES5 CommonJS 目标
npm install @reactivex/ix-es5-esm # ES5 ESModules 目标
npm install @reactivex/ix-es5-umd # ES5 UMD 目标
npm install @reactivex/ix-es2015-cjs # ES2015 CommonJS 目标
npm install @reactivex/ix-es2015-esm # ES2015 ESModules 目标
npm install @reactivex/ix-es2015-umd # ES2015 UMD 目标
npm install @reactivex/ix-esnext-cjs # ESNext CommonJS 目标
npm install @reactivex/ix-esnext-esm # ESNext ESModules 目标
npm install @reactivex/ix-esnext-umd # ESNext UMD 目标
我们为什么这样打包
JS 社区是一个多样化的群体,有各种各样的目标环境和工具链。发布多个包可以满足各种类型的项目需求。针对最新 JS 运行时的朋友可以使用 ESNext + ESM 构建。需要广泛浏览器支持和小下载体积的朋友可以使用 UMD 包,它已经通过 Google 的 Closure Compiler 进行了高级优化。
如果你认为我们遗漏了某个编译目标,而这阻碍了你的采用,请开一个 issue。我们随时为你服务 ❤️。
许可证
MIT 许可证 (MIT)
版权所有 (c) ReactiveX
特此免费授予任何获得本软件及相关文档文件("软件")副本的人不受限制地处理本软件的权利,包括但不限于使用、复制、修改、合并、发布、分发、再许可和/或销售软件副本的权利,以及允许向其提供软件的人这样做,但须符合以下条件:
上述版权声明和本许可声明应包含在软件的所有副本或主要部分中。
软件按"原样"提供,不提供任何形式的明示或暗示的保证,包括但不限于对适销性、特定用途适用性和非侵权性的保证。在任何情况下,作者或版权持有人均不对任何索赔、损害或其他责任负责,无论这些追责来自合同、侵权或其它行为中,还是产生于、源于或有关于本软件以及本软件的使用或其它处置。