Skip to content

Commit 7245005

Browse files
justinwookwonoj
authored andcommitted
feat(from): allow Observable.from to handle array-like objects
this brings the signature and functionality closer to `Observable.from` that is in RxJS4.
1 parent 2c65ed4 commit 7245005

File tree

3 files changed

+153
-2
lines changed

3 files changed

+153
-2
lines changed

spec/observables/from-spec.js

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,67 @@ describe('Observable.from', function () {
1313
}, null, done);
1414
}, 300);
1515

16+
it('should handle an ArrayLike', function (done) {
17+
var arrayLike = {
18+
length: 3,
19+
0: 1,
20+
1: 2,
21+
2: 3
22+
};
23+
var expected = [1, 2, 3];
24+
var i = 0;
25+
Observable.from(arrayLike).subscribe(function (x) {
26+
expect(x).toBe(expected[i++]);
27+
}, null, done);
28+
}, 300);
29+
30+
it('should handle an ArrayLike from arguments', function (done) {
31+
function makeArrayLike() {
32+
var expected = [1, 2, 3];
33+
var i = 0;
34+
35+
Observable.from(arguments).subscribe(function (x) {
36+
expect(x).toBe(expected[i++]);
37+
}, null, done);
38+
}
39+
40+
makeArrayLike(1, 2, 3);
41+
}, 300);
42+
43+
it('should handle an ArrayLike with a mapFn', function (done) {
44+
var arrayLike = {
45+
length: 3,
46+
0: 1,
47+
1: 2,
48+
2: 3
49+
};
50+
var expected = [1, 1, 1];
51+
var i = 0;
52+
var mapFn = function (v, k) {
53+
return v - k;
54+
};
55+
Observable.from(arrayLike, mapFn).subscribe(function (x) {
56+
expect(x).toBe(expected[i++]);
57+
}, null, done);
58+
}, 300);
59+
60+
it('should handle an ArrayLike with a thisArg', function (done) {
61+
var arrayLike = {
62+
length: 3,
63+
0: 1,
64+
1: 2,
65+
2: 3
66+
};
67+
var expected = [123, 123, 123];
68+
var i = 0;
69+
var mapFn = function (x, y) {
70+
return this.thing;
71+
};
72+
Observable.from(arrayLike, mapFn, {thing: 123}).subscribe(function (x) {
73+
expect(x).toBe(expected[i++]);
74+
}, null, done);
75+
});
76+
1677
it('should handle a promise', function (done) {
1778
var promise = Promise.resolve('pinky swear');
1879

src/observable/ArrayLikeObservable.ts

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
import {Scheduler} from '../Scheduler';
2+
import {Observable} from '../Observable';
3+
import {ScalarObservable} from './ScalarObservable';
4+
import {EmptyObservable} from './EmptyObservable';
5+
import {Subscriber} from '../Subscriber';
6+
import {Subscription} from '../Subscription';
7+
8+
export class ArrayLikeObservable<T> extends Observable<T> {
9+
10+
private mapFn: (x: any, y: number) => T;
11+
12+
static create<T>(arrayLike: ArrayLike<T>, mapFn: (x: any, y: number) => T, thisArg: any, scheduler?: Scheduler): Observable<T> {
13+
const length = arrayLike.length;
14+
if (length === 0) {
15+
return new EmptyObservable<T>();
16+
} else if (length === 1 && !mapFn) {
17+
return new ScalarObservable<T>(<any>arrayLike[0], scheduler);
18+
} else {
19+
return new ArrayLikeObservable(arrayLike, mapFn, thisArg, scheduler);
20+
}
21+
}
22+
23+
static dispatch(state: any) {
24+
const { arrayLike, index, length, mapFn, subscriber } = state;
25+
26+
if (subscriber.isUnsubscribed) {
27+
return;
28+
}
29+
30+
if (index >= length) {
31+
subscriber.complete();
32+
return;
33+
}
34+
35+
const result = mapFn ? mapFn(arrayLike[index], index) : arrayLike[index];
36+
subscriber.next(result);
37+
38+
state.index = index + 1;
39+
40+
(<any> this).schedule(state);
41+
}
42+
43+
// value used if Array has one value and _isScalar
44+
private value: any;
45+
46+
constructor(private arrayLike: ArrayLike<T>, mapFn: (x: any, y: number) => T, thisArg: any, private scheduler?: Scheduler) {
47+
super();
48+
if (!mapFn && !scheduler && arrayLike.length === 1) {
49+
this._isScalar = true;
50+
this.value = arrayLike[0];
51+
}
52+
if (mapFn) {
53+
this.mapFn = mapFn.bind(thisArg);
54+
}
55+
}
56+
57+
protected _subscribe(subscriber: Subscriber<T>): Subscription | Function | void {
58+
let index = 0;
59+
const { arrayLike, mapFn, scheduler } = this;
60+
const length = arrayLike.length;
61+
62+
if (scheduler) {
63+
return scheduler.schedule(ArrayLikeObservable.dispatch, 0, {
64+
arrayLike, index, length, mapFn, subscriber
65+
});
66+
} else {
67+
for (let i = 0; i < length && !subscriber.isUnsubscribed; i++) {
68+
const result = mapFn ? mapFn(arrayLike[i], i) : arrayLike[i];
69+
subscriber.next(result);
70+
}
71+
subscriber.complete();
72+
}
73+
}
74+
}

src/observable/FromObservable.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,33 +1,49 @@
11
import {isArray} from '../util/isArray';
2+
import {isFunction} from '../util/isFunction';
23
import {isPromise} from '../util/isPromise';
4+
import {isScheduler} from '../util/isScheduler';
35
import {PromiseObservable} from './PromiseObservable';
46
import {IteratorObservable} from'./IteratorObservable';
57
import {ArrayObservable} from './ArrayObservable';
8+
import {ArrayLikeObservable} from './ArrayLikeObservable';
69

710
import {Scheduler} from '../Scheduler';
811
import {SymbolShim} from '../util/SymbolShim';
912
import {Observable} from '../Observable';
1013
import {Subscriber} from '../Subscriber';
1114
import {ObserveOnSubscriber} from '../operator/observeOn';
1215

16+
const isArrayLike = (<T>(x: any): x is ArrayLike<T> => x && typeof x.length === 'number');
17+
1318
export class FromObservable<T> extends Observable<T> {
1419
constructor(private ish: Observable<T> | Promise<T> | Iterator<T> | ArrayLike<T>, private scheduler: Scheduler) {
1520
super(null);
1621
}
1722

18-
static create<T>(ish: any, scheduler: Scheduler = null): Observable<T> {
23+
static create<T>(ish: any, mapFnOrScheduler: Scheduler | ((x: any, y: number) => T), thisArg?: any, lastScheduler?: Scheduler): Observable<T> {
24+
let scheduler: Scheduler = null;
25+
let mapFn: (x: number, y: any) => T = null;
26+
if (isFunction(mapFnOrScheduler)) {
27+
scheduler = lastScheduler || null;
28+
mapFn = <(x: number, y: any) => T> mapFnOrScheduler;
29+
} else if (isScheduler(scheduler)) {
30+
scheduler = <Scheduler> mapFnOrScheduler;
31+
}
32+
1933
if (ish != null) {
2034
if (typeof ish[SymbolShim.observable] === 'function') {
2135
if (ish instanceof Observable && !scheduler) {
2236
return ish;
2337
}
2438
return new FromObservable(ish, scheduler);
25-
} if (isArray(ish)) {
39+
} else if (isArray(ish)) {
2640
return new ArrayObservable(ish, scheduler);
2741
} else if (isPromise(ish)) {
2842
return new PromiseObservable(ish, scheduler);
2943
} else if (typeof ish[SymbolShim.iterator] === 'function' || typeof ish === 'string') {
3044
return new IteratorObservable<T>(<any>ish, null, null, scheduler);
45+
} else if (isArrayLike(ish)) {
46+
return new ArrayLikeObservable(ish, mapFn, thisArg, scheduler);
3147
}
3248
}
3349

0 commit comments

Comments
 (0)