UNPKG

13 kBMarkdownView Raw
1# The Interactive Extensions for JavaScript (IxJS)
2
3[![Greenkeeper badge](https://badges.greenkeeper.io/ReactiveX/IxJS.svg)](https://greenkeeper.io/)
4[![Build Status](https://travis-ci.org/ReactiveX/IxJS.svg?branch=master)](https://travis-ci.org/ReactiveX/IxJS)
5[![Build status](https://ci.appveyor.com/api/projects/status/dfuqvf29l477m54k/branch/master?svg=true)](https://ci.appveyor.com/project/mattpodwysocki/ixjs/branch/master)
6[![npm version](https://badge.fury.io/js/ix.svg)](https://badge.fury.io/js/ix)
7[![Join the chat at https://gitter.im/ReactiveXIxJS/](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/ReactiveXIxJS/?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)
8
9*IxJS is a set of libraries to compose synchronous and asynchronous collections and [Array#extras](http://blogs.msdn.com/b/ie/archive/2010/12/13/ecmascript-5-part-2-array-extras.aspx) style composition in JavaScript*
10
11The Interactive Extensions for JavaScript (IxJS) brings the Array#extras combinators to iterables, generators, async iterables and async generators. With the introduction of the `Symbol.iterator` and generators in ES2015, and subsequent introduction of `Symbol.asyncIterator` and async generators, it became obvious we need an abstraction over these data structures for composition, querying and more.
12
13IxJS unifies both synchronous and asynchronous pull-based collections, just as RxJS unified the world of push-based collections. RxJS is great for event-based workflows where the data can be pushed at the rate of the producer, however, IxJS is great at I/O operations where you as the consumer can pull the data when you are ready.
14
15## Install [IxJS from npm](https://www.npmjs.com/package/ix)
16
17```sh
18npm install ix
19```
20
21(also read about how we [package IxJS](#packaging) below)
22
23## `Iterable`
24
25The `Iterable` class a way to create and compose synchronous collections much like Arrays, Maps and Sets in JavaScript using the Array#extras style using the familiar methods you are used to like `map`, `filter`, `reduce` and more.
26
27```js
28// ES
29import * as Ix from 'ix';
30
31// CommonJS
32const Ix = require('ix');
33
34Ix.Iterable.from([1,2,3,4])
35 .filter(x => x % 2 === 0)
36 .map(x => x * 2)
37 .forEach(x => console.log(`Next ${x}`));
38
39// => 4
40// => 8
41```
42
43Alternatively, we can use the [`for ... of`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/for...of) statements to iterate our collections.
44
45```js
46// ES
47import * as Ix from 'ix';
48
49// CommonJS
50const Ix = require('ix');
51
52const results = Ix.Iterable.from([1,2,3,4])
53 .filter(x => x % 2 === 0)
54 .map(x => x * 2);
55
56for (let item of results) {
57 console.log(`Next ${item}`);
58}
59
60// => 4
61// => 8
62```
63
64Instead of bringing in the entire library for `Iterable`, we can pick and choose which operators we want, for bundling concerns.
65
66```js
67// ES
68import { IterableX as Iterable } from 'ix/iterable';
69import 'ix/add/iterable-operators/map';
70
71// CommonJS
72const Iterable = require('ix/iterable').IterableX;
73require('ix/add/iterable-operators/map');
74
75const results = Iterable.of(1,2,3)
76 .map(x => x + '!!');
77```
78
79We can also bring in only the operators that we want to using just the operators themselves. Many of these operators take a simple `Iterable` source such as an `Array`, `Map`, `Set` or generator function such as our `map` and `filter` functions.
80
81```js
82// ES
83import { map } from 'ix/iterable/map';
84import { filter } from 'ix/iterable/filter';
85
86// CommonJS
87const map = require('ix/iterable/map').map;
88const filter = require('ix/iterable/filter').filter;
89
90const source = [1,2,3];
91const results = map(
92 filter(
93 source,
94 x => x % 2 === 0
95 ),
96 x => x * x
97);
98
99for (let item of results) {
100 console.log(`Next: ${item}`);
101}
102
103// Next 4
104```
105
106Just like RxJS, IxJS supports "lettable" operators which allow you to chain together operators, keeping the surface area to a minimum on the `Iterable` object.
107
108```js
109// ES
110import { IterableX as Iterable } from 'ix/iterable';
111import { map, filter } from 'ix/iterable/pipe';
112
113// CommonJS
114const Iterable = require('ix/iterable').IterableX;
115const { map, filter } = require('ix/iterable/pipe');
116
117const results = of(1, 2, 3).pipe(
118 filter(x => x % 2 === 0),
119 map(x => x * x)
120);
121
122for (let item of results) {
123 console.log(`Next: ${item}`);
124}
125```
126
127The `Iterable` object implements the iterator pattern in JavaScript by exposing the `[Symbol.iterator]` method which in turn exposes the `Iterator` class. The iterator yields values by calling the `next()` method which returns the `IteratorResult` class.
128
129```typescript
130interface Iterable<T> {
131 [Symbol.iterator](): Iterator<T>;
132}
133
134interface Iterator<T> {
135 next(value?: any): IteratorResult<T>;
136 return?(value?: any): IteratorResult<T>;
137 throw?(e?: any): IteratorResult<T>;
138}
139
140interface IteratorResult<T> {
141 value: T;
142 done: Boolean;
143}
144```
145
146## `AsyncIterable`
147
148The `AsyncIterable` object is based off the ECMAScript Proposal for [Asynchronous Iterators](https://github.com/tc39/proposal-async-iteration). This would allow us to create asynchronous collections of Promises and be able to use such methods as the `map`, `filter`, `reduce` and other Array#extras methods that you are used to using.
149
150```js
151import * as Ix from 'ix';
152
153// CommonJS
154const Ix = require('ix');
155
156async function* gen() {
157 yield 1;
158 yield 2;
159 yield 3;
160 yield 4;
161}
162
163Ix.AsyncIterable.from(gen())
164 .filter(x => x % 2 === 0)
165 .map(x => x * 2)
166 .forEach(x => console.log(`Next ${x}`))
167 .catch(err => console.log(`Error ${err}`));
168
169// => Next 4
170// => Next 8
171```
172
173Much like with the `Iterable` object where we can iterate through our collections, we can use `for await ... of` instead which allows us to iterate over the asynchronous collection.
174
175```js
176import * as Ix from 'ix';
177
178// CommonJS
179const Ix = require('ix');
180
181async function* gen() {
182 yield 1;
183 yield 2;
184 yield 3;
185 yield 4;
186}
187
188const results = Ix.AsyncIterable.from(gen())
189 .filter(x => x % 2 === 0)
190 .map(x => x * 2);
191
192for await (let item of results) {
193 console.log(`Next ${x}`);
194}
195
196// => Next 4
197// => Next 8
198```
199
200Instead of bringing in the entire library for `AsyncIterable`, we can pick and choose which operators we want, for bundling concerns.
201
202```js
203// ES
204import { AsyncIterableX as AsyncIterable } from 'ix/asynciterable';
205import 'ix/add/asynciterable-operators/map';
206
207// CommonJS
208const AsyncIterable = require('ix/asynciterable').AsyncIterableX;
209require('ix/add/asynciterable-operators/map');
210
211const results = AsyncIterable.of(1,2,3)
212 .map(x => x + '!!');
213```
214
215We can also bring in only the operators that we want to using just the operators themselves. Many of these operators take a simple `AsyncIterable` source from `async function*` functions such as the `map` and `filter` functions.
216
217```js
218// ES
219import { map } from 'ix/asynciterable/map';
220import { filter } from 'ix/asynciterable/filter';
221
222// CommonJS
223const map = require('ix/asynciterable/map').map;
224const filter = require('ix/asynciterable/filter').filter;
225
226const source = async function* () {
227 yield 1;
228 yield 2;
229 yield 3;
230 yield 4;
231};
232
233const results = map(
234 filter(
235 source(),
236 x => x % 2 === 0
237 ),
238 x => x * x
239);
240
241for await (let item of results) {
242 console.log(`Next: ${item}`);
243}
244```
245
246Just like RxJS, IxJS supports "lettable" operators which allow you to chain together operators, keeping the surface area to a minimum on the `AsyncIterable` object.
247
248```js
249// ES
250import { AsyncIterableX as AsyncIterable } from 'ix/asynciterable';
251import { filter, map } from 'ix/asynciterable/pipe';
252
253// CommonJS
254const AsyncIterable = require('ix/asynciterable').AsyncIterableX;
255const { filter, map } = require('ix/asynciterable/pipe');
256
257const source = async function* () {
258 yield 1;
259 yield 2;
260 yield 3;
261 yield 4;
262};
263
264const results = from(source()).pipe(
265 filter(async x => x % 2 === 0),
266 map(async x => x * x)
267);
268
269for await (let item of results) {
270 console.log(`Next: ${item}`);
271}
272```
273
274The `AsyncIterable` class implements the async iterator pattern in JavaScript by exposing the `[Symbol.asyncIterator]` method which in turn exposes the `AsyncIterator` class. The iterator yields values by calling the `next()` method which returns a Promise which resolves a `IteratorResult` class.
275
276```typescript
277interface AsyncIterable<T> {
278 [Symbol.asyncIterator](): AsyncIterator<T>;
279}
280
281interface AsyncIterator<T> {
282 [Symbol.asyncIterator](): AsyncIterator<T>;
283 next(value?: any): Promise<IteratorResult<T>>;
284 return?(value?: any): Promise<IteratorResult<T>>;
285 throw?(e?: any): Promise<IteratorResult<T>>;
286}
287
288interface IteratorResult<T> {
289 value: T;
290 done: Boolean;
291}
292```
293
294## Converting from Iterable to AsyncIterable
295
296Using IxJS, you can easily go from an `Iterable` to an `AsyncIterable` using a number of methods. First, we can use the `from` function, either as a standalone or on the `Ix.AsyncIterable` object. The `from` method accepts a standard `Iterable`, `Generator`, and `Iterator` of Promises, or even another `AsyncIterable`.
297
298```js
299import { from } from 'ix/asynciterable/from';
300import { map } from 'ix/asynciterable/map';
301
302const xs = [1, 2, 3, 4];
303const asyncIterable = from(xs);
304
305const mapped = map(asyncIterable, async (item, index) => item * index);
306
307for await (let item of mapped) {
308 console.log(`Next: ${item}`);
309}
310```
311
312In addition, you can use the specialized async methods that are suffixed with `Async`, such as `mapAsync`, `filterAsync`, `flatMapAsync` amongst others. These functions accept async functions which allow you to return a `Promise` as the result.
313
314```js
315import { mapAsync } from 'ix/iterable/mapasync';
316
317const xs = [1, 2, 3, 4];
318const mapped = mapAsync(xs, async (item, index) => item * index);
319
320for await (let item of mapped) {
321 console.log(`Next: ${item}`);
322}
323```
324
325## Contributing
326
327We are grateful for contributions to the IxJS project. The IxJS project evolves because of community involvemnent from people such as yourselves. Please read below on how to get involved.
328
329### [Code Of Conduct](CODE_OF_CONDUCT.md)
330
331The IxJS project has a strict Code of Conduct that must be adhered at all times. This code of conduct comes from the [Contributor Convenant](http://contributor-covenant.org/). Please read [the full text](CODE_OF_CONDUCT.md) as to what is and is not permitted.
332
333### Contributing Guide
334
335Read the [Contributing Guide](CONTRIBUTING.md) on how to get involved with the IxJS project. This includes our development process and how to test your code before committing.
336
337### Packaging
338
339`IxJS` is written in TypeScript, but the project is compiled to multiple JS versions and common module formats. The base IxJS package includes all the compilation targets for convenience, but if you're conscientious about your node_modules footprint, don't worry -- we got you. The targets are also published under the @reactivex namespace:
340
341```sh
342npm install @reactivex/ix-ts # TypeScript target
343npm install @reactivex/ix-es5-cjs # ES5 CommonJS target
344npm install @reactivex/ix-es5-esm # ES5 ESModules target
345npm install @reactivex/ix-es5-umd # ES5 UMD target
346npm install @reactivex/ix-es2015-cjs # ES2015 CommonJS target
347npm install @reactivex/ix-es2015-esm # ES2015 ESModules target
348npm install @reactivex/ix-es2015-umd # ES2015 UMD target
349npm install @reactivex/ix-esnext-esm # ESNext CommonJS target
350npm install @reactivex/ix-esnext-esm # ESNext ESModules target
351npm install @reactivex/ix-esnext-umd # ESNext UMD target
352```
353
354### Why we package like this
355
356The JS community is a diverse group with a varied list of target environments and tool chains. Publishing multiple packages accommodates projects of all types. Friends targeting the latest JS runtimes can pull in the ESNext + ESM build. Friends needing wide browser support and small download size can use the UMD bundle, which has been run through Google's Closure Compiler with advanced optimizations.
357
358If you think we missed a compilation target and it's a blocker for adoption, please open an issue. We're here for you ❤️.
359
360## License
361
362The MIT License (MIT)
363
364Copyright (c) ReactiveX
365
366Permission is hereby granted, free of charge, to any person obtaining a copy
367of this software and associated documentation files (the "Software"), to deal
368in the Software without restriction, including without limitation the rights
369to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
370copies of the Software, and to permit persons to whom the Software is
371furnished to do so, subject to the following conditions:
372
373The above copyright notice and this permission notice shall be included in all
374copies or substantial portions of the Software.
375
376THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
377IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
378FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
379AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
380LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
381OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
382SOFTWARE.