diff --git a/src/observable/dom/AjaxObservable.ts b/src/observable/dom/AjaxObservable.ts index ec6b60bb3b..89647acbc9 100644 --- a/src/observable/dom/AjaxObservable.ts +++ b/src/observable/dom/AjaxObservable.ts @@ -4,7 +4,7 @@ import { errorObject } from '../../util/errorObject'; import { Observable } from '../../Observable'; import { Subscriber } from '../../Subscriber'; import { TeardownLogic } from '../../Subscription'; -import { MapOperator } from '../../operator/map'; +import { map } from '../../operators'; export interface AjaxRequest { url?: string; @@ -87,9 +87,17 @@ export function ajaxPatch(url: string, body?: any, headers?: Object): Observable return new AjaxObservable({ method: 'PATCH', url, body, headers }); }; +const mapResponse = map((x: AjaxResponse, index: number) => x.response); + export function ajaxGetJSON(url: string, headers?: Object): Observable { - return new AjaxObservable({ method: 'GET', url, responseType: 'json', headers }) - .lift(new MapOperator((x: AjaxResponse, index: number): T => x.response, null)); + return mapResponse( + new AjaxObservable({ + method: 'GET', + url, + responseType: 'json', + headers + }) + ); }; /** diff --git a/src/operator/map.ts b/src/operator/map.ts index cf49b6b776..03879dde50 100644 --- a/src/operator/map.ts +++ b/src/operator/map.ts @@ -1,5 +1,4 @@ -import { Operator } from '../Operator'; -import { Subscriber } from '../Subscriber'; +import { map as higherOrderMap } from '../operators'; import { Observable } from '../Observable'; /** @@ -36,47 +35,5 @@ import { Observable } from '../Observable'; * @owner Observable */ export function map(this: Observable, project: (value: T, index: number) => R, thisArg?: any): Observable { - if (typeof project !== 'function') { - throw new TypeError('argument is not a function. Are you looking for `mapTo()`?'); - } - return this.lift(new MapOperator(project, thisArg)); -} - -export class MapOperator implements Operator { - constructor(private project: (value: T, index: number) => R, private thisArg: any) { - } - - call(subscriber: Subscriber, source: any): any { - return source.subscribe(new MapSubscriber(subscriber, this.project, this.thisArg)); - } -} - -/** - * We need this JSDoc comment for affecting ESDoc. - * @ignore - * @extends {Ignored} - */ -class MapSubscriber extends Subscriber { - count: number = 0; - private thisArg: any; - - constructor(destination: Subscriber, - private project: (value: T, index: number) => R, - thisArg: any) { - super(destination); - this.thisArg = thisArg || this; - } - - // NOTE: This looks unoptimized, but it's actually purposefully NOT - // using try/catch optimizations. - protected _next(value: T) { - let result: any; - try { - result = this.project.call(this.thisArg, value, this.count++); - } catch (err) { - this.destination.error(err); - return; - } - this.destination.next(result); - } + return higherOrderMap(project, thisArg)(this); } diff --git a/src/operators/index.ts b/src/operators/index.ts new file mode 100644 index 0000000000..2e82fdaac3 --- /dev/null +++ b/src/operators/index.ts @@ -0,0 +1 @@ +export { map, OperatorFunction } from './map'; diff --git a/src/operators/map.ts b/src/operators/map.ts new file mode 100644 index 0000000000..034d41b17d --- /dev/null +++ b/src/operators/map.ts @@ -0,0 +1,88 @@ +import { Operator } from '../Operator'; +import { Subscriber } from '../Subscriber'; +import { Observable } from '../Observable'; + +export interface OperatorFunction { + (source: Observable): Observable; +} + +/** + * Applies a given `project` function to each value emitted by the source + * Observable, and emits the resulting values as an Observable. + * + * Like [Array.prototype.map()](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map), + * it passes each source value through a transformation function to get + * corresponding output values. + * + * + * + * Similar to the well known `Array.prototype.map` function, this operator + * applies a projection to each value and emits that projection in the output + * Observable. + * + * @example Map every click to the clientX position of that click + * var clicks = Rx.Observable.fromEvent(document, 'click'); + * var positions = clicks.map(ev => ev.clientX); + * positions.subscribe(x => console.log(x)); + * + * @see {@link mapTo} + * @see {@link pluck} + * + * @param {function(value: T, index: number): R} project The function to apply + * to each `value` emitted by the source Observable. The `index` parameter is + * the number `i` for the i-th emission that has happened since the + * subscription, starting from the number `0`. + * @param {any} [thisArg] An optional argument to define what `this` is in the + * `project` function. + * @return {Observable} An Observable that emits the values from the source + * Observable transformed by the given `project` function. + * @method map + * @owner Observable + */ +export function map(project: (value: T, index: number) => R, thisArg?: any): OperatorFunction { + return function mapOperation(source: Observable): Observable { + if (typeof project !== 'function') { + throw new TypeError('argument is not a function. Are you looking for `mapTo()`?'); + } + return source.lift(new MapOperator(project, thisArg)); + }; +} + +export class MapOperator implements Operator { + constructor(private project: (value: T, index: number) => R, private thisArg: any) { + } + + call(subscriber: Subscriber, source: any): any { + return source.subscribe(new MapSubscriber(subscriber, this.project, this.thisArg)); + } +} + +/** + * We need this JSDoc comment for affecting ESDoc. + * @ignore + * @extends {Ignored} + */ +class MapSubscriber extends Subscriber { + count: number = 0; + private thisArg: any; + + constructor(destination: Subscriber, + private project: (value: T, index: number) => R, + thisArg: any) { + super(destination); + this.thisArg = thisArg || this; + } + + // NOTE: This looks unoptimized, but it's actually purposefully NOT + // using try/catch optimizations. + protected _next(value: T) { + let result: any; + try { + result = this.project.call(this.thisArg, value, this.count++); + } catch (err) { + this.destination.error(err); + return; + } + this.destination.next(result); + } +}