forked from staltz/toy-rx
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
0 parents
commit bf6036d
Showing
30 changed files
with
977 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,2 @@ | ||
node_modules/ | ||
.vscode/ |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
/** | ||
* A Subscription that can group other child Subscriptions together. | ||
*/ | ||
function GroupSubscription() { | ||
this.subscriptions = []; | ||
} | ||
|
||
GroupSubscription.prototype.add = function add(subscription) { | ||
this.subscriptions.push(subscription); | ||
}; | ||
|
||
GroupSubscription.prototype.unsubscribe = function unsubscribe() { | ||
this.subscriptions.forEach(function (subscription) { | ||
subscription.unsubscribe(); | ||
}); | ||
} | ||
|
||
module.exports = GroupSubscription; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
The MIT License (MIT) | ||
|
||
Copyright (c) 2017 André Staltz | ||
|
||
Permission is hereby granted, free of charge, to any person obtaining a copy | ||
of this software and associated documentation files (the "Software"), to deal | ||
in the Software without restriction, including without limitation the rights | ||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | ||
copies of the Software, and to permit persons to whom the Software is | ||
furnished to do so, subject to the following conditions: | ||
|
||
The above copyright notice and this permission notice shall be included in all | ||
copies or substantial portions of the Software. | ||
|
||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | ||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | ||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | ||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | ||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | ||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE | ||
SOFTWARE. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,153 @@ | ||
# Toy RxJS | ||
|
||
A tiny implementation of RxJS that actually works, for learning. | ||
|
||
## Usage | ||
|
||
`npm install toy-rx` | ||
|
||
```js | ||
const Rx = require('toy-rx') | ||
Rx.Observable.fromEvent = require('toy-rx/fromEvent') | ||
Rx.Observable.prototype.map = require('toy-rx/map') | ||
Rx.Observable.prototype.filter = require('toy-rx/filter') | ||
Rx.Observable.prototype.delay = require('toy-rx/delay') | ||
|
||
Rx.Observable.fromEvent(document, 'click') | ||
.delay(500) | ||
.map(ev => ev.clientX) | ||
.filter(x => x < 200) | ||
.subscribe({ | ||
next: x => console.log(x), | ||
error: e => console.error(e), | ||
complete: () => {}, | ||
}) | ||
``` | ||
|
||
## Why | ||
|
||
I made this so people can look into the implementation of a simple RxJS and feel like they can actually understand it. I mean, the implementation is literally this below: | ||
|
||
```js | ||
class Subscription { | ||
constructor(unsubscribe) { | ||
this.unsubscribe = unsubscribe; | ||
} | ||
} | ||
|
||
class Subscriber extends Subscription { | ||
constructor(observer) { | ||
super(function unsubscribe() {}); | ||
this.observer = observer; | ||
} | ||
|
||
next(x) { | ||
this.observer.next(x); | ||
} | ||
|
||
error(e) { | ||
this.observer.error(e); | ||
this.unsubscribe(); | ||
} | ||
|
||
complete() { | ||
this.observer.complete(); | ||
this.unsubscribe(); | ||
} | ||
} | ||
|
||
class Observable { | ||
constructor(subscribe) { | ||
this.subscribe = subscribe; | ||
} | ||
|
||
static create(subscribe) { | ||
return new Observable(function internalSubscribe(observer) { | ||
const subscriber = new Subscriber(observer); | ||
const subscription = subscribe(subscriber); | ||
subscriber.unsubscribe = subscription.unsubscribe.bind(subscription); | ||
return subscription; | ||
}); | ||
} | ||
} | ||
|
||
class Subject extends Observable { | ||
constructor() { | ||
super(function subscribe(observer) { | ||
this.observers.push(observer); | ||
return new Subscription(() => { | ||
const index = this.observers.indexOf(observer); | ||
if (index >= 0) this.observers.splice(index, 1); | ||
}); | ||
}); | ||
this.observers = []; | ||
} | ||
|
||
next(x) { | ||
this.observers.forEach((observer) => observer.next(x)); | ||
} | ||
|
||
error(e) { | ||
this.observers.forEach((observer) => observer.error(e)); | ||
} | ||
|
||
complete() { | ||
this.observers.forEach((observer) => observer.complete()); | ||
} | ||
} | ||
``` | ||
|
||
See? It fit easily in a README and you weren't scared of looking at it even though it's source code. | ||
|
||
Where are all the operators? Well, here's `map` for instance: | ||
|
||
```js | ||
function map(transformFn) { | ||
const inObservable = this; | ||
const outObservable = Rx.Observable.create(function subscribe(outObserver) { | ||
const inObserver = { | ||
next: (x) => { | ||
try { | ||
var y = transformFn(x); | ||
} catch (e) { | ||
outObserver.error(e); | ||
return; | ||
} | ||
outObserver.next(y); | ||
}, | ||
error: (e) => { | ||
outObserver.error(e); | ||
}, | ||
complete: () => { | ||
outObserver.complete(); | ||
} | ||
}; | ||
return inObservable.subscribe(inObserver); | ||
}); | ||
return outObservable; | ||
} | ||
``` | ||
|
||
What about `filter`, and `combineLatest` and all the rest? Just look for yourself, don't be afraid to click on the JS files in this project. | ||
|
||
## How is this different to the official RxJS? | ||
|
||
This is just meant for education. It's missing a ton of stuff that the [official RxJS](http://reactivex.io/rxjs/) covers, such as: | ||
|
||
- Subscribe supports partial Observer objects | ||
- Subscribe supports functions as arguments | ||
- Dozens of useful operators | ||
- The wonderful and underrated [Lift Architecture](https://github.com/ReactiveX/rxjs/blob/3ced24f2192289e441e88368c02c9df86bcb11e0/src/Observable.ts#L60-L72) | ||
- Schedulers (and in turn, testing with marble diagrams) | ||
- Corner cases covered against bugs | ||
- Thorough documentation and examples | ||
- Thorough tests | ||
- TypeScript support | ||
|
||
Overall, this project is a simplification of RxJS which describes "close enough" how RxJS works, but has a lot of holes and bugs because we deliberately are not taking care of many corner cases in this code. | ||
|
||
Use this project to gain confidence peeking into the implementation of a library and get familiar with RxJS internals. | ||
|
||
## Contributing | ||
|
||
Don't bother submitting PRs for this project to add more operators and more bug safety. Let's just keep this simple enough to read for any developer with a few minutes of free time. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
const Rx = require('./index'); | ||
const GroupSubscription = require('./GroupSubscription'); | ||
|
||
/** | ||
* Rx.Observable.combineLatest = require('toy-rx/combineLatest'); | ||
*/ | ||
module.exports = function combineLatest() { | ||
const inObservables = Array.prototype.slice.call(arguments); | ||
const transformFn = inObservables.pop(); | ||
const outObservable = Rx.Observable.create(function subscribe(outObserver) { | ||
const values = inObservables.map((inObservable) => undefined); | ||
const gotValue = inObservables.map((inObservable) => false); | ||
const outSubscription = new GroupSubscription(); | ||
inObservables.forEach((inObservable, index) => { | ||
const inObserver = { | ||
next: (x) => { | ||
values[index] = x; | ||
gotValue[index] = true; | ||
if (gotValue.every(x => x === true)) { | ||
try { | ||
var y = transformFn(...values); | ||
} catch (e) { | ||
outObserver.error(e); | ||
return | ||
} | ||
outObserver.next(y); | ||
} | ||
}, | ||
error: (e) => { | ||
outObserver.error(e); | ||
}, | ||
complete: () => { | ||
outObserver.complete(); | ||
} | ||
} | ||
outSubscription.add(inObservable.subscribe(inObserver)); | ||
}); | ||
return outSubscription; | ||
}); | ||
return outObservable; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
const Rx = require('./index'); | ||
|
||
/** | ||
* Rx.Observable.prototype.delay = require('toy-rx/delay'); | ||
*/ | ||
module.exports = function delay(period) { | ||
const inObservable = this; | ||
const outObservable = Rx.Observable.create(function subscribe(outObserver) { | ||
const inObserver = { | ||
next: (x) => { | ||
setTimeout(() => outObserver.next(x), period); | ||
}, | ||
error: (e) => { | ||
setTimeout(() => outObserver.error(e), period); | ||
}, | ||
complete: () => { | ||
setTimeout(() => outObserver.complete(), period); | ||
} | ||
}; | ||
return inObservable.subscribe(inObserver); | ||
}); | ||
return outObservable; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
const Rx = require('./index'); | ||
|
||
/** | ||
* Rx.Observable.empty = require('toy-rx/empty'); | ||
*/ | ||
module.exports = function empty() { | ||
return Rx.Observable.create(function subscribe(observer) { | ||
observer.complete(); | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
const Rx = require('./index'); | ||
|
||
/** | ||
* Rx.Observable.prototype.filter = require('toy-rx/filter'); | ||
*/ | ||
module.exports = function filter(conditionFn) { | ||
const inObservable = this; | ||
const outObservable = Rx.Observable.create(function subscribe(outObserver) { | ||
const inObserver = { | ||
next: (x) => { | ||
try { | ||
var passed = conditionFn(x); | ||
} catch (e) { | ||
outObserver.error(e); | ||
return; | ||
} | ||
if (passed) { | ||
outObserver.next(x); | ||
} | ||
}, | ||
error: (e) => { | ||
outObserver.error(e); | ||
}, | ||
complete: () => { | ||
outObserver.complete(); | ||
} | ||
}; | ||
return inObservable.subscribe(inObserver); | ||
}); | ||
return outObservable; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
const Rx = require('./index'); | ||
|
||
/** | ||
* Rx.Observable.fromArray = require('toy-rx/fromArray'); | ||
*/ | ||
module.exports = function fromArray(array) { | ||
return Rx.Observable.create(function subscribe(observer) { | ||
array.forEach(x => observer.next(x)); | ||
observer.complete(); | ||
return new Rx.Subscription(function unsubscribe() {}); | ||
}); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
const Rx = require('./index'); | ||
|
||
/** | ||
* Rx.Observable.fromEvent = require('toy-rx/fromEvent'); | ||
*/ | ||
module.exports = function fromEvent(eventTarget, eventType) { | ||
return Rx.Observable.create(function subscribe(observer) { | ||
eventTarget.addEventListener(eventType, observer.next); | ||
return new Rx.Subscription(function unsubscribe() { | ||
eventTarget.removeEventListener(eventType, observer.next); | ||
}); | ||
}); | ||
} |
Oops, something went wrong.