d982735c4794ef2c4a107fe6100499c647004d439b259c4f091070db4e106dbf693d384113ebe14541affec153a99e512f713a97931f8547ff65612c8cb35a 4.0 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495
  1. import { OperatorFunction } from '../types';
  2. import { operate } from '../util/lift';
  3. import { scanInternals } from './scanInternals';
  4. export function scan<V, A = V>(accumulator: (acc: A | V, value: V, index: number) => A): OperatorFunction<V, V | A>;
  5. export function scan<V, A>(accumulator: (acc: A, value: V, index: number) => A, seed: A): OperatorFunction<V, A>;
  6. export function scan<V, A, S>(accumulator: (acc: A | S, value: V, index: number) => A, seed: S): OperatorFunction<V, A>;
  7. // TODO: link to a "redux pattern" section in the guide (location TBD)
  8. /**
  9. * Useful for encapsulating and managing state. Applies an accumulator (or "reducer function")
  10. * to each value from the source after an initial state is established -- either via
  11. * a `seed` value (second argument), or from the first value from the source.
  12. *
  13. * <span class="informal">It's like {@link reduce}, but emits the current
  14. * accumulation state after each update</span>
  15. *
  16. * ![](scan.png)
  17. *
  18. * This operator maintains an internal state and emits it after processing each value as follows:
  19. *
  20. * 1. First value arrives
  21. * - If a `seed` value was supplied (as the second argument to `scan`), let `state = seed` and `value = firstValue`.
  22. * - If NO `seed` value was supplied (no second argument), let `state = firstValue` and go to 3.
  23. * 2. Let `state = accumulator(state, value)`.
  24. * - If an error is thrown by `accumulator`, notify the consumer of an error. The process ends.
  25. * 3. Emit `state`.
  26. * 4. Next value arrives, let `value = nextValue`, go to 2.
  27. *
  28. * ## Examples
  29. *
  30. * An average of previous numbers. This example shows how
  31. * not providing a `seed` can prime the stream with the
  32. * first value from the source.
  33. *
  34. * ```ts
  35. * import { of, scan, map } from 'rxjs';
  36. *
  37. * const numbers$ = of(1, 2, 3);
  38. *
  39. * numbers$
  40. * .pipe(
  41. * // Get the sum of the numbers coming in.
  42. * scan((total, n) => total + n),
  43. * // Get the average by dividing the sum by the total number
  44. * // received so far (which is 1 more than the zero-based index).
  45. * map((sum, index) => sum / (index + 1))
  46. * )
  47. * .subscribe(console.log);
  48. * ```
  49. *
  50. * The Fibonacci sequence. This example shows how you can use
  51. * a seed to prime accumulation process. Also... you know... Fibonacci.
  52. * So important to like, computers and stuff that its whiteboarded
  53. * in job interviews. Now you can show them the Rx version! (Please don't, haha)
  54. *
  55. * ```ts
  56. * import { interval, scan, map, startWith } from 'rxjs';
  57. *
  58. * const firstTwoFibs = [0, 1];
  59. * // An endless stream of Fibonacci numbers.
  60. * const fibonacci$ = interval(1000).pipe(
  61. * // Scan to get the fibonacci numbers (after 0, 1)
  62. * scan(([a, b]) => [b, a + b], firstTwoFibs),
  63. * // Get the second number in the tuple, it's the one you calculated
  64. * map(([, n]) => n),
  65. * // Start with our first two digits :)
  66. * startWith(...firstTwoFibs)
  67. * );
  68. *
  69. * fibonacci$.subscribe(console.log);
  70. * ```
  71. *
  72. * @see {@link expand}
  73. * @see {@link mergeScan}
  74. * @see {@link reduce}
  75. * @see {@link switchScan}
  76. *
  77. * @param accumulator A "reducer function". This will be called for each value after an initial state is
  78. * acquired.
  79. * @param seed The initial state. If this is not provided, the first value from the source will
  80. * be used as the initial state, and emitted without going through the accumulator. All subsequent values
  81. * will be processed by the accumulator function. If this is provided, all values will go through
  82. * the accumulator function.
  83. * @return A function that returns an Observable of the accumulated values.
  84. */
  85. export function scan<V, A, S>(accumulator: (acc: V | A | S, value: V, index: number) => A, seed?: S): OperatorFunction<V, V | A> {
  86. // providing a seed of `undefined` *should* be valid and trigger
  87. // hasSeed! so don't use `seed !== undefined` checks!
  88. // For this reason, we have to check it here at the original call site
  89. // otherwise inside Operator/Subscriber we won't know if `undefined`
  90. // means they didn't provide anything or if they literally provided `undefined`
  91. return operate(scanInternals(accumulator, seed as S, arguments.length >= 2, true));
  92. }