| import { Subscriber } from '../Subscriber'; |
| import { EmptyError } from '../util/EmptyError'; |
| /** |
| * Emits only the first value (or the first value that meets some condition) |
| * emitted by the source Observable. |
| * |
| * <span class="informal">Emits only the first value. Or emits only the first |
| * value that passes some test.</span> |
| * |
| * <img src="./img/first.png" width="100%"> |
| * |
| * If called with no arguments, `first` emits the first value of the source |
| * Observable, then completes. If called with a `predicate` function, `first` |
| * emits the first value of the source that matches the specified condition. It |
| * may also take a `resultSelector` function to produce the output value from |
| * the input value, and a `defaultValue` to emit in case the source completes |
| * before it is able to emit a valid value. Throws an error if `defaultValue` |
| * was not provided and a matching element is not found. |
| * |
| * @example <caption>Emit only the first click that happens on the DOM</caption> |
| * var clicks = Rx.Observable.fromEvent(document, 'click'); |
| * var result = clicks.first(); |
| * result.subscribe(x => console.log(x)); |
| * |
| * @example <caption>Emits the first click that happens on a DIV</caption> |
| * var clicks = Rx.Observable.fromEvent(document, 'click'); |
| * var result = clicks.first(ev => ev.target.tagName === 'DIV'); |
| * result.subscribe(x => console.log(x)); |
| * |
| * @see {@link filter} |
| * @see {@link find} |
| * @see {@link take} |
| * |
| * @throws {EmptyError} Delivers an EmptyError to the Observer's `error` |
| * callback if the Observable completes before any `next` notification was sent. |
| * |
| * @param {function(value: T, index: number, source: Observable<T>): boolean} [predicate] |
| * An optional function called with each item to test for condition matching. |
| * @param {function(value: T, index: number): R} [resultSelector] A function to |
| * produce the value on the output Observable based on the values |
| * and the indices of the source Observable. The arguments passed to this |
| * function are: |
| * - `value`: the value that was emitted on the source. |
| * - `index`: the "index" of the value from the source. |
| * @param {R} [defaultValue] The default value emitted in case no valid value |
| * was found on the source. |
| * @return {Observable<T|R>} An Observable of the first item that matches the |
| * condition. |
| * @method first |
| * @owner Observable |
| */ |
| export function first(predicate, resultSelector, defaultValue) { |
| return (source) => source.lift(new FirstOperator(predicate, resultSelector, defaultValue, source)); |
| } |
| class FirstOperator { |
| constructor(predicate, resultSelector, defaultValue, source) { |
| this.predicate = predicate; |
| this.resultSelector = resultSelector; |
| this.defaultValue = defaultValue; |
| this.source = source; |
| } |
| call(observer, source) { |
| return source.subscribe(new FirstSubscriber(observer, this.predicate, this.resultSelector, this.defaultValue, this.source)); |
| } |
| } |
| /** |
| * We need this JSDoc comment for affecting ESDoc. |
| * @ignore |
| * @extends {Ignored} |
| */ |
| class FirstSubscriber extends Subscriber { |
| constructor(destination, predicate, resultSelector, defaultValue, source) { |
| super(destination); |
| this.predicate = predicate; |
| this.resultSelector = resultSelector; |
| this.defaultValue = defaultValue; |
| this.source = source; |
| this.index = 0; |
| this.hasCompleted = false; |
| this._emitted = false; |
| } |
| _next(value) { |
| const index = this.index++; |
| if (this.predicate) { |
| this._tryPredicate(value, index); |
| } |
| else { |
| this._emit(value, index); |
| } |
| } |
| _tryPredicate(value, index) { |
| let result; |
| try { |
| result = this.predicate(value, index, this.source); |
| } |
| catch (err) { |
| this.destination.error(err); |
| return; |
| } |
| if (result) { |
| this._emit(value, index); |
| } |
| } |
| _emit(value, index) { |
| if (this.resultSelector) { |
| this._tryResultSelector(value, index); |
| return; |
| } |
| this._emitFinal(value); |
| } |
| _tryResultSelector(value, index) { |
| let result; |
| try { |
| result = this.resultSelector(value, index); |
| } |
| catch (err) { |
| this.destination.error(err); |
| return; |
| } |
| this._emitFinal(result); |
| } |
| _emitFinal(value) { |
| const destination = this.destination; |
| if (!this._emitted) { |
| this._emitted = true; |
| destination.next(value); |
| destination.complete(); |
| this.hasCompleted = true; |
| } |
| } |
| _complete() { |
| const destination = this.destination; |
| if (!this.hasCompleted && typeof this.defaultValue !== 'undefined') { |
| destination.next(this.defaultValue); |
| destination.complete(); |
| } |
| else if (!this.hasCompleted) { |
| destination.error(new EmptyError); |
| } |
| } |
| } |
| //# sourceMappingURL=first.js.map |