switchMap.js 5.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. import { OuterSubscriber } from '../OuterSubscriber';
  2. import { subscribeToResult } from '../util/subscribeToResult';
  3. /* tslint:enable:max-line-length */
  4. /**
  5. * Projects each source value to an Observable which is merged in the output
  6. * Observable, emitting values only from the most recently projected Observable.
  7. *
  8. * <span class="informal">Maps each value to an Observable, then flattens all of
  9. * these inner Observables using {@link switch}.</span>
  10. *
  11. * <img src="./img/switchMap.png" width="100%">
  12. *
  13. * Returns an Observable that emits items based on applying a function that you
  14. * supply to each item emitted by the source Observable, where that function
  15. * returns an (so-called "inner") Observable. Each time it observes one of these
  16. * inner Observables, the output Observable begins emitting the items emitted by
  17. * that inner Observable. When a new inner Observable is emitted, `switchMap`
  18. * stops emitting items from the earlier-emitted inner Observable and begins
  19. * emitting items from the new one. It continues to behave like this for
  20. * subsequent inner Observables.
  21. *
  22. * @example <caption>Rerun an interval Observable on every click event</caption>
  23. * var clicks = Rx.Observable.fromEvent(document, 'click');
  24. * var result = clicks.switchMap((ev) => Rx.Observable.interval(1000));
  25. * result.subscribe(x => console.log(x));
  26. *
  27. * @see {@link concatMap}
  28. * @see {@link exhaustMap}
  29. * @see {@link mergeMap}
  30. * @see {@link switch}
  31. * @see {@link switchMapTo}
  32. *
  33. * @param {function(value: T, ?index: number): ObservableInput} project A function
  34. * that, when applied to an item emitted by the source Observable, returns an
  35. * Observable.
  36. * @param {function(outerValue: T, innerValue: I, outerIndex: number, innerIndex: number): any} [resultSelector]
  37. * A function to produce the value on the output Observable based on the values
  38. * and the indices of the source (outer) emission and the inner Observable
  39. * emission. The arguments passed to this function are:
  40. * - `outerValue`: the value that came from the source
  41. * - `innerValue`: the value that came from the projected Observable
  42. * - `outerIndex`: the "index" of the value that came from the source
  43. * - `innerIndex`: the "index" of the value from the projected Observable
  44. * @return {Observable} An Observable that emits the result of applying the
  45. * projection function (and the optional `resultSelector`) to each item emitted
  46. * by the source Observable and taking only the values from the most recently
  47. * projected inner Observable.
  48. * @method switchMap
  49. * @owner Observable
  50. */
  51. export function switchMap(project, resultSelector) {
  52. return function switchMapOperatorFunction(source) {
  53. return source.lift(new SwitchMapOperator(project, resultSelector));
  54. };
  55. }
  56. class SwitchMapOperator {
  57. constructor(project, resultSelector) {
  58. this.project = project;
  59. this.resultSelector = resultSelector;
  60. }
  61. call(subscriber, source) {
  62. return source.subscribe(new SwitchMapSubscriber(subscriber, this.project, this.resultSelector));
  63. }
  64. }
  65. /**
  66. * We need this JSDoc comment for affecting ESDoc.
  67. * @ignore
  68. * @extends {Ignored}
  69. */
  70. class SwitchMapSubscriber extends OuterSubscriber {
  71. constructor(destination, project, resultSelector) {
  72. super(destination);
  73. this.project = project;
  74. this.resultSelector = resultSelector;
  75. this.index = 0;
  76. }
  77. _next(value) {
  78. let result;
  79. const index = this.index++;
  80. try {
  81. result = this.project(value, index);
  82. }
  83. catch (error) {
  84. this.destination.error(error);
  85. return;
  86. }
  87. this._innerSub(result, value, index);
  88. }
  89. _innerSub(result, value, index) {
  90. const innerSubscription = this.innerSubscription;
  91. if (innerSubscription) {
  92. innerSubscription.unsubscribe();
  93. }
  94. this.add(this.innerSubscription = subscribeToResult(this, result, value, index));
  95. }
  96. _complete() {
  97. const { innerSubscription } = this;
  98. if (!innerSubscription || innerSubscription.closed) {
  99. super._complete();
  100. }
  101. }
  102. /** @deprecated internal use only */ _unsubscribe() {
  103. this.innerSubscription = null;
  104. }
  105. notifyComplete(innerSub) {
  106. this.remove(innerSub);
  107. this.innerSubscription = null;
  108. if (this.isStopped) {
  109. super._complete();
  110. }
  111. }
  112. notifyNext(outerValue, innerValue, outerIndex, innerIndex, innerSub) {
  113. if (this.resultSelector) {
  114. this._tryNotifyNext(outerValue, innerValue, outerIndex, innerIndex);
  115. }
  116. else {
  117. this.destination.next(innerValue);
  118. }
  119. }
  120. _tryNotifyNext(outerValue, innerValue, outerIndex, innerIndex) {
  121. let result;
  122. try {
  123. result = this.resultSelector(outerValue, innerValue, outerIndex, innerIndex);
  124. }
  125. catch (err) {
  126. this.destination.error(err);
  127. return;
  128. }
  129. this.destination.next(result);
  130. }
  131. }
  132. //# sourceMappingURL=switchMap.js.map