Skip to content

Commit 2adefda

Browse files
committed
Merge branch 'next'
2 parents 2a9e7e0 + 4a274b4 commit 2adefda

22 files changed

+493
-215
lines changed

README.md

Lines changed: 4 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -49,75 +49,10 @@ NEXT NUMBER: 50 // after a 1 second delay
4949
I AM COMPLETE // same time as previous line
5050
```
5151

52-
### There a few plugins that are available - each return a new Observable instance:
52+
### There a few plugins that are available - each return a new Observable instance
5353

54-
- They are called by doing: `observableInstance$.<pluginFunction>`
54+
- [Documentation on the plugin operators can be seen here](./src/operators/DOCUMENTATION.md)
5555

56-
`.map(<mapCallback>)`
56+
### There are also a few Observables that can be created from the Observable object itself
5757

58-
- will map each value to a new value using the callback
59-
60-
`.filter(<filterCallback>)`
61-
62-
- will filter out values that you do not want to subscribe to
63-
64-
`.do(<doCallback>)`
65-
66-
- will run some callback before passing the current value to the subscription
67-
68-
`.take(<amount>[, <callback>])`
69-
70-
- will take the amount of values you want (or less if it completes first) and then complete -- this is useful for infinitely running observables
71-
- a callback may be passed to filter out the valeus you want to take
72-
73-
`.first([<callback>])`
74-
75-
- this is an alias for `.take(1[, <callback>])`
76-
77-
`.toPromise()`
78-
79-
- this will do a `.first()` on the observable object and return the value from that to the response from a promise
80-
81-
`.flatMap(<mapCallback>)`
82-
83-
- same as `.map(<mapCallback>)` but will take the value of the callback and turn it from an observable to a value
84-
85-
`.switchMap(<mapCallback>)`
86-
87-
- the value from the mapCallback is an observable and if another value comes through the previous observable is cancelled
88-
- this is useful for things like typeaheads where you dont want a request for every keypress
89-
90-
`.delay(<time>)`
91-
92-
- will delay output from the observable until a specific time interval has passed
93-
94-
`.debounceTime(<time>)`
95-
96-
- will only output values if there has not been any new values in the past time interval passed
97-
98-
### There are also a few Observables that can be created from the Observable object itself:
99-
100-
- They are called by doing: `Observable.<observableFunction>`
101-
102-
`.fromEvent(<eventName>, <element>[, <mapCallback>)`
103-
104-
- will create an observable that will listen for an event on a DOM element
105-
- there is also an optional mappCallback that can be used to map the event that comes through the next observer function
106-
107-
`.fromPromise(<promise>)`
108-
109-
- turns a promise into an observable that emits the value of the promise and then completes
110-
111-
`.interval(<time>[, <start = 0>])`
112-
113-
- emits a number at the specified time interval and increases by one every time
114-
- there is an optional start value that can be passed
115-
116-
`.of(<...values>)`
117-
118-
- takes any number of arguments and emits them in an observable (in order) and then completes
119-
120-
`.range(<start>[, <end>])`
121-
122-
- emits a range of numbers from start to end and then completes
123-
- if only start is given than the range starts at 0 and ends at the start value
58+
- [Documentation on the observables can be seen here](./src/observables/DOCUMENTATION.md)

example/index.js

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,24 +1,28 @@
11
const { Observable } = Servable;
22

3-
// const countObservable$ = Observable.interval(1000, 1);
4-
//
5-
// const countSubscription = countObservable$
6-
// .take(10)
7-
// .map((n) => n * 5)
8-
// .filter((n) => n > 10)
9-
// .subscribe({
10-
// next (number) {
11-
// console.log('NEXT NUMBER: ', number);
12-
// },
13-
//
14-
// error (errors) {
15-
// console.warn('I HAVE ERRORS', errors)
16-
// },
17-
//
18-
// complete () {
19-
// console.log('I AM COMPLETE');
20-
// }
21-
// });
3+
const countObservable$ = Observable.interval(1000, 1);
4+
5+
const countSubscription = countObservable$
6+
.take(10)
7+
.map((n) => n * 5)
8+
.filter((n) => n > 10)
9+
.combineLatest(
10+
countObservable$
11+
.take(5)
12+
)
13+
.subscribe({
14+
next (number) {
15+
console.log('NEXT NUMBER: ', number);
16+
},
17+
18+
error (errors) {
19+
console.warn('I HAVE ERRORS', errors)
20+
},
21+
22+
complete () {
23+
console.log('I AM COMPLETE');
24+
}
25+
});
2226

2327
// test event binding
2428
const inputObservable$ = Observable.fromEvent('input', document.getElementById('myInput'), (event) => event.currentTarget.value);

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "servable",
3-
"version": "0.1.5",
3+
"version": "0.2.0",
44
"description": "From scratch observable",
55
"main": "dist/index.js",
66
"scripts": {

src/Observable.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ export class Observable {
66
this.observerCallback = observerCallback;
77
}
88

9+
static create (observerCallback) {
10+
return new Observable(observerCallback);
11+
}
12+
913
subscribe (next = noop, error = noop, complete = noop) {
1014
return new Subscription(this.observerCallback, { next, error, complete });
1115
}

src/Observer.js

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
import { noop } from './utilities';
2+
3+
export class Observer {
4+
constructor (observer = {}) {
5+
this.isComplete = false;
6+
this.dispose = noop;
7+
8+
this.setupObserver(observer);
9+
}
10+
11+
cleanup () {
12+
this.catchErrors(() => {
13+
this.dispose();
14+
15+
this.onNext = noop;
16+
this.onError = noop;
17+
this.onComplete = noop;
18+
this.dispose = noop;
19+
20+
this.isComplete = true;
21+
})();
22+
23+
return this;
24+
}
25+
26+
catchErrors (callback) {
27+
return (...args) => {
28+
try {
29+
return callback(...args);
30+
} catch (errors) {
31+
this.onError(errors);
32+
}
33+
};
34+
}
35+
36+
use (callback) {
37+
return this.catchErrors(() => {
38+
const response = callback({
39+
next: (...args) => this.onNext(...args),
40+
error: (...errors) => this.onError(...errors),
41+
complete: () => this.onComplete(),
42+
});
43+
44+
if (typeof response === 'function') {
45+
this.dispose = response;
46+
} else {
47+
this.dispose = noop;
48+
}
49+
50+
return this.dispose;
51+
})();
52+
}
53+
54+
setupObserver ({ next = noop, error = noop, complete = noop }) {
55+
// assumes that an object was passed as first value to subscription
56+
if (typeof next !== 'function' && typeof next === 'object') {
57+
return this.setupObserver(next);
58+
}
59+
60+
this.onNext = this.catchErrors((...args) => {
61+
if (!this.isComplete) {
62+
return next(...args);
63+
}
64+
});
65+
66+
this.onError = (...errors) => {
67+
if (!this.isComplete) {
68+
return error(...errors);
69+
}
70+
};
71+
72+
this.onComplete = this.catchErrors(() => {
73+
if (!this.isComplete) {
74+
this.cleanup();
75+
return complete();
76+
}
77+
});
78+
79+
return this;
80+
}
81+
}

src/Subject.js

Lines changed: 30 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,49 +1,64 @@
1-
import { Subscription } from './Subscription';
2-
import { noop } from './utilities';
1+
import {Subscription} from './Subscription';
32

43
export class Subject {
54
constructor () {
65
this.next = this.next.bind(this);
76
this.error = this.error.bind(this);
87
this.complete = this.complete.bind(this);
98

10-
this.subscriptionList = [];
9+
this.observers = [];
1110
}
1211

13-
subscribe (next = noop, error = noop, complete = noop) {
14-
const subscription = new Subscription(noop, {next, error, complete});
12+
subscribe (observer) {
13+
const subscription = Subscription.createSimple(observer);
1514

16-
this.subscriptionList.push(subscription);
15+
this.observers.push(subscription.observer);
1716

1817
return subscription;
1918
}
2019

20+
unsubscribe () {
21+
this
22+
.cleanup((observer) => {
23+
observer.cleanup();
24+
25+
return false;
26+
});
27+
}
28+
2129
cleanup (callback) {
22-
this.subscriptionList = this.subscriptionList.filter((subscription) => {
23-
if (!subscription.isComplete) {
24-
callback(subscription);
30+
this.observers = this.observers.filter((observer) => {
31+
if (!observer.isComplete) {
32+
const ret = callback(observer);
33+
34+
if (typeof ret === 'boolean') {
35+
return ret;
36+
}
37+
2538
return true;
2639
}
2740

2841
return false;
2942
});
43+
44+
return this;
3045
}
3146

3247
next (...args) {
33-
this.cleanup((subscription) => {
34-
subscription.next(...args);
48+
this.cleanup((observer) => {
49+
observer.onNext(...args);
3550
});
3651
}
3752

3853
error (...errors) {
39-
this.cleanup((subscription) => {
40-
subscription.error(...errors);
54+
this.cleanup((observer) => {
55+
observer.onError(...errors);
4156
});
4257
}
4358

4459
complete () {
45-
this.cleanup((subscription) => {
46-
subscription.complete();
60+
this.cleanup((observer) => {
61+
observer.onComplete();
4762
});
4863
}
4964
}

0 commit comments

Comments
 (0)