Skip to content

Commit d026c41

Browse files
committed
perf(distinct): increase perf from 60% of Rx4 to 1000% Rx4
- removed closure - custom tryCatching (all in _next) - removed HashSet impl in favor of plain array
1 parent e97fc7b commit d026c41

File tree

1 file changed

+39
-54
lines changed

1 file changed

+39
-54
lines changed

src/operator/distinct.ts

Lines changed: 39 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@ import {Observable} from '../Observable';
22
import {Operator} from '../Operator';
33
import {Subscriber} from '../Subscriber';
44
import {Subscription} from '../Subscription';
5-
import {tryCatch} from '../util/tryCatch';
6-
import {errorObject} from '../util/errorObject';
75

86
/**
97
* Returns an Observable that emits all items emitted by the source Observable that are distinct by comparison from previous items.
@@ -28,84 +26,71 @@ class DistinctOperator<T, R> implements Operator<T, R> {
2826
}
2927
}
3028

31-
class HashSet<T> {
32-
private set: Array<T> = [];
33-
34-
constructor(private compare: (x: T, y: T) => boolean) {
35-
}
36-
37-
private has(item: T): boolean {
38-
for (var i = 0; i < this.set.length; i++) {
39-
if (this.compare(this.set[i], item)) {
40-
return true;
41-
}
42-
}
43-
44-
return false;
45-
}
46-
47-
push(item: T): boolean {
48-
if (this.has(item)) {
49-
return false;
50-
} else {
51-
this.set.push(item);
52-
return true;
53-
}
54-
}
55-
56-
flush(): void {
57-
this.set = [];
58-
}
59-
}
60-
61-
class DistinctSubscriber<T> extends Subscriber<T> {
62-
private hashSet: HashSet<T>;
29+
export class DistinctSubscriber<T> extends Subscriber<T> {
30+
private values: any[] = [];
6331
private flushSubscription: Subscription;
6432

6533
constructor(destination: Subscriber<T>, compare: (x: T, y: T) => boolean, flushes: Observable<any>) {
6634
super(destination);
6735
if (typeof compare === 'function') {
6836
this.compare = compare;
6937
}
70-
this.hashSet = new HashSet(this.compare);
7138

7239
if (flushes) {
73-
this.flushSubscription = flushes.subscribe(() => this.hashSet.flush());
40+
this.add(this.flushSubscription = flushes.subscribe(new FlushSubscriber(this)));
7441
}
7542
}
7643

77-
private compare(x: T, y: T): boolean {
78-
return x === y;
44+
flush() {
45+
this.values.length = 0;
7946
}
8047

81-
private disposeFlushSubscription(): void {
82-
if (this.flushSubscription) {
83-
this.flushSubscription.unsubscribe();
84-
}
48+
private compare(x: T, y: T): boolean {
49+
return x === y;
8550
}
8651

8752
protected _next(value: T): void {
88-
let result: any = false;
89-
90-
result = tryCatch(this.hashSet.push.bind(this.hashSet))(value);
91-
if (result === errorObject) {
92-
this.destination.error(errorObject.e);
53+
let found = false;
54+
const values = this.values;
55+
const len = values.length;
56+
try {
57+
for (let i = 0; i < len; i++) {
58+
if (this.compare(values[i], value)) {
59+
found = true;
60+
return;
61+
}
62+
}
63+
} catch (err) {
64+
this.destination.error(err);
9365
return;
9466
}
95-
96-
if (result) {
97-
this.destination.next(value);
98-
}
67+
this.values.push(value);
68+
this.destination.next(value);
9969
}
10070

10171
protected _complete(): void {
102-
this.disposeFlushSubscription();
10372
super._complete();
10473
}
10574

10675
unsubscribe(): void {
107-
this.disposeFlushSubscription();
10876
super.unsubscribe();
10977
}
110-
11178
}
79+
80+
export class FlushSubscriber extends Subscriber<any> {
81+
constructor(private parent: DistinctSubscriber<any>) {
82+
super();
83+
}
84+
85+
next() {
86+
this.parent.flush();
87+
}
88+
89+
complete() {
90+
// noop
91+
}
92+
93+
error(err: any) {
94+
this.parent.error(err);
95+
}
96+
}

0 commit comments

Comments
 (0)