Repositorio del curso CCOM4030 el semestre B91 del proyecto Artesanías con el Instituto de Cultura

throttleTime.ts 5.8KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174
  1. import { Operator } from '../Operator';
  2. import { Subscriber } from '../Subscriber';
  3. import { Subscription } from '../Subscription';
  4. import { async } from '../scheduler/async';
  5. import { Observable } from '../Observable';
  6. import { ThrottleConfig, defaultThrottleConfig } from './throttle';
  7. import { MonoTypeOperatorFunction, SchedulerLike, TeardownLogic } from '../types';
  8. /**
  9. * Emits a value from the source Observable, then ignores subsequent source
  10. * values for `duration` milliseconds, then repeats this process.
  11. *
  12. * <span class="informal">Lets a value pass, then ignores source values for the
  13. * next `duration` milliseconds.</span>
  14. *
  15. * ![](throttleTime.png)
  16. *
  17. * `throttleTime` emits the source Observable values on the output Observable
  18. * when its internal timer is disabled, and ignores source values when the timer
  19. * is enabled. Initially, the timer is disabled. As soon as the first source
  20. * value arrives, it is forwarded to the output Observable, and then the timer
  21. * is enabled. After `duration` milliseconds (or the time unit determined
  22. * internally by the optional `scheduler`) has passed, the timer is disabled,
  23. * and this process repeats for the next source value. Optionally takes a
  24. * {@link SchedulerLike} for managing timers.
  25. *
  26. * ## Examples
  27. *
  28. * #### Limit click rate
  29. *
  30. * Emit clicks at a rate of at most one click per second
  31. * ```ts
  32. * import { fromEvent } from 'rxjs';
  33. * import { throttleTime } from 'rxjs/operators';
  34. *
  35. * const clicks = fromEvent(document, 'click');
  36. * const result = clicks.pipe(throttleTime(1000));
  37. * result.subscribe(x => console.log(x));
  38. * ```
  39. *
  40. * #### Double Click
  41. *
  42. * The following example only emits clicks which happen within a subsequent
  43. * delay of 400ms of the previous click. This for example can emulate a double
  44. * click. It makes use of the `trailing` parameter of the throttle configuration.
  45. *
  46. * ```ts
  47. * import { fromEvent, asyncScheduler } from 'rxjs';
  48. * import { throttleTime, withLatestFrom } from 'rxjs/operators';
  49. *
  50. * // defaultThottleConfig = { leading: true, trailing: false }
  51. * const throttleConfig = {
  52. * leading: false,
  53. * trailing: true
  54. * }
  55. *
  56. * const click = fromEvent(document, 'click');
  57. * const doubleClick = click.pipe(
  58. * throttleTime(400, asyncScheduler, throttleConfig)
  59. * );
  60. *
  61. * doubleClick.subscribe((throttleValue: Event) => {
  62. * console.log(`Double-clicked! Timestamp: ${throttleValue.timeStamp}`);
  63. * });
  64. * ```
  65. *
  66. * If you enable the `leading` parameter in this example, the output would be the primary click and
  67. * the double click, but restricts additional clicks within 400ms.
  68. *
  69. * @see {@link auditTime}
  70. * @see {@link debounceTime}
  71. * @see {@link delay}
  72. * @see {@link sampleTime}
  73. * @see {@link throttle}
  74. *
  75. * @param {number} duration Time to wait before emitting another value after
  76. * emitting the last value, measured in milliseconds or the time unit determined
  77. * internally by the optional `scheduler`.
  78. * @param {SchedulerLike} [scheduler=async] The {@link SchedulerLike} to use for
  79. * managing the timers that handle the throttling.
  80. * @param {Object} config a configuration object to define `leading` and
  81. * `trailing` behavior. Defaults to `{ leading: true, trailing: false }`.
  82. * @return {Observable<T>} An Observable that performs the throttle operation to
  83. * limit the rate of emissions from the source.
  84. * @method throttleTime
  85. * @owner Observable
  86. */
  87. export function throttleTime<T>(duration: number,
  88. scheduler: SchedulerLike = async,
  89. config: ThrottleConfig = defaultThrottleConfig): MonoTypeOperatorFunction<T> {
  90. return (source: Observable<T>) => source.lift(new ThrottleTimeOperator(duration, scheduler, config.leading, config.trailing));
  91. }
  92. class ThrottleTimeOperator<T> implements Operator<T, T> {
  93. constructor(private duration: number,
  94. private scheduler: SchedulerLike,
  95. private leading: boolean,
  96. private trailing: boolean) {
  97. }
  98. call(subscriber: Subscriber<T>, source: any): TeardownLogic {
  99. return source.subscribe(
  100. new ThrottleTimeSubscriber(subscriber, this.duration, this.scheduler, this.leading, this.trailing)
  101. );
  102. }
  103. }
  104. /**
  105. * We need this JSDoc comment for affecting ESDoc.
  106. * @ignore
  107. * @extends {Ignored}
  108. */
  109. class ThrottleTimeSubscriber<T> extends Subscriber<T> {
  110. private throttled: Subscription;
  111. private _hasTrailingValue: boolean = false;
  112. private _trailingValue: T = null;
  113. constructor(destination: Subscriber<T>,
  114. private duration: number,
  115. private scheduler: SchedulerLike,
  116. private leading: boolean,
  117. private trailing: boolean) {
  118. super(destination);
  119. }
  120. protected _next(value: T) {
  121. if (this.throttled) {
  122. if (this.trailing) {
  123. this._trailingValue = value;
  124. this._hasTrailingValue = true;
  125. }
  126. } else {
  127. this.add(this.throttled = this.scheduler.schedule<DispatchArg<T>>(dispatchNext, this.duration, { subscriber: this }));
  128. if (this.leading) {
  129. this.destination.next(value);
  130. } else if (this.trailing) {
  131. this._trailingValue = value;
  132. this._hasTrailingValue = true;
  133. }
  134. }
  135. }
  136. protected _complete() {
  137. if (this._hasTrailingValue) {
  138. this.destination.next(this._trailingValue);
  139. this.destination.complete();
  140. } else {
  141. this.destination.complete();
  142. }
  143. }
  144. clearThrottle() {
  145. const throttled = this.throttled;
  146. if (throttled) {
  147. if (this.trailing && this._hasTrailingValue) {
  148. this.destination.next(this._trailingValue);
  149. this._trailingValue = null;
  150. this._hasTrailingValue = false;
  151. }
  152. throttled.unsubscribe();
  153. this.remove(throttled);
  154. this.throttled = null;
  155. }
  156. }
  157. }
  158. interface DispatchArg<T> {
  159. subscriber: ThrottleTimeSubscriber<T>;
  160. }
  161. function dispatchNext<T>(arg: DispatchArg<T>) {
  162. const { subscriber } = arg;
  163. subscriber.clearThrottle();
  164. }