last.ts
5.35 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
import { Observable } from '../Observable';
import { Operator } from '../Operator';
import { Subscriber } from '../Subscriber';
import { EmptyError } from '../util/EmptyError';
/* tslint:disable:max-line-length */
export function last<T, S extends T>(this: Observable<T>,
predicate: (value: T, index: number, source: Observable<T>) => value is S): Observable<S>;
export function last<T, S extends T, R>(this: Observable<T>,
predicate: (value: T | S, index: number, source: Observable<T>) => value is S,
resultSelector: (value: S, index: number) => R, defaultValue?: R): Observable<R>;
export function last<T, S extends T>(this: Observable<T>,
predicate: (value: T, index: number, source: Observable<T>) => value is S,
resultSelector: void,
defaultValue?: S): Observable<S>;
export function last<T>(this: Observable<T>,
predicate?: (value: T, index: number, source: Observable<T>) => boolean): Observable<T>;
export function last<T, R>(this: Observable<T>,
predicate: (value: T, index: number, source: Observable<T>) => boolean,
resultSelector?: (value: T, index: number) => R,
defaultValue?: R): Observable<R>;
export function last<T>(this: Observable<T>,
predicate: (value: T, index: number, source: Observable<T>) => boolean,
resultSelector: void,
defaultValue?: T): Observable<T>;
/* tslint:disable:max-line-length */
/**
* Returns an Observable that emits only the last item emitted by the source Observable.
* It optionally takes a predicate function as a parameter, in which case, rather than emitting
* the last item from the source Observable, the resulting Observable will emit the last item
* from the source Observable that satisfies the predicate.
*
* <img src="./img/last.png" width="100%">
*
* @throws {EmptyError} Delivers an EmptyError to the Observer's `error`
* callback if the Observable completes before any `next` notification was sent.
* @param {function} predicate - the condition any source emitted item has to satisfy.
* @return {Observable} an Observable that emits only the last item satisfying the given condition
* from the source, or an NoSuchElementException if no such items are emitted.
* @throws - Throws if no items that match the predicate are emitted by the source Observable.
* @method last
* @owner Observable
*/
export function last<T, R>(this: Observable<T>, predicate?: (value: T, index: number, source: Observable<T>) => boolean,
resultSelector?: ((value: T, index: number) => R) | void,
defaultValue?: R): Observable<T | R> {
return this.lift(new LastOperator(predicate, resultSelector, defaultValue, this));
}
class LastOperator<T, R> implements Operator<T, R> {
constructor(private predicate?: (value: T, index: number, source: Observable<T>) => boolean,
private resultSelector?: ((value: T, index: number) => R) | void,
private defaultValue?: any,
private source?: Observable<T>) {
}
call(observer: Subscriber<R>, source: any): any {
return source.subscribe(new LastSubscriber(observer, this.predicate, this.resultSelector, this.defaultValue, this.source));
}
}
/**
* We need this JSDoc comment for affecting ESDoc.
* @ignore
* @extends {Ignored}
*/
class LastSubscriber<T, R> extends Subscriber<T> {
private lastValue: T | R;
private hasValue: boolean = false;
private index: number = 0;
constructor(destination: Subscriber<R>,
private predicate?: (value: T, index: number, source: Observable<T>) => boolean,
private resultSelector?: ((value: T, index: number) => R) | void,
private defaultValue?: any,
private source?: Observable<T>) {
super(destination);
if (typeof defaultValue !== 'undefined') {
this.lastValue = defaultValue;
this.hasValue = true;
}
}
protected _next(value: T): void {
const index = this.index++;
if (this.predicate) {
this._tryPredicate(value, index);
} else {
if (this.resultSelector) {
this._tryResultSelector(value, index);
return;
}
this.lastValue = value;
this.hasValue = true;
}
}
private _tryPredicate(value: T, index: number) {
let result: any;
try {
result = this.predicate(value, index, this.source);
} catch (err) {
this.destination.error(err);
return;
}
if (result) {
if (this.resultSelector) {
this._tryResultSelector(value, index);
return;
}
this.lastValue = value;
this.hasValue = true;
}
}
private _tryResultSelector(value: T, index: number) {
let result: any;
try {
result = (<any>this).resultSelector(value, index);
} catch (err) {
this.destination.error(err);
return;
}
this.lastValue = result;
this.hasValue = true;
}
protected _complete(): void {
const destination = this.destination;
if (this.hasValue) {
destination.next(this.lastValue);
destination.complete();
} else {
destination.error(new EmptyError);
}
}
}