3c6a1b8f5f769752cd3e95146a49d4e43044e6b21e7f1bf1cf2bf79e2474c5c9c1db03811289ceda84871abb3f27f94e2c9cd041240a84429298ff1d0b1ca0 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. import { Operator } from '../Operator';
  2. import { Observable } from '../Observable';
  3. import { Subscriber } from '../Subscriber';
  4. import { Subscription } from '../Subscription';
  5. import { async } from '../scheduler/async';
  6. import { MonoTypeOperatorFunction, SchedulerLike, TeardownLogic } from '../types';
  7. /**
  8. * Emits a value from the source Observable only after a particular time span
  9. * has passed without another source emission.
  10. *
  11. * <span class="informal">It's like {@link delay}, but passes only the most
  12. * recent value from each burst of emissions.</span>
  13. *
  14. * ![](debounceTime.png)
  15. *
  16. * `debounceTime` delays values emitted by the source Observable, but drops
  17. * previous pending delayed emissions if a new value arrives on the source
  18. * Observable. This operator keeps track of the most recent value from the
  19. * source Observable, and emits that only when `dueTime` enough time has passed
  20. * without any other value appearing on the source Observable. If a new value
  21. * appears before `dueTime` silence occurs, the previous value will be dropped
  22. * and will not be emitted on the output Observable.
  23. *
  24. * This is a rate-limiting operator, because it is impossible for more than one
  25. * value to be emitted in any time window of duration `dueTime`, but it is also
  26. * a delay-like operator since output emissions do not occur at the same time as
  27. * they did on the source Observable. Optionally takes a {@link SchedulerLike} for
  28. * managing timers.
  29. *
  30. * ## Example
  31. * Emit the most recent click after a burst of clicks
  32. * ```ts
  33. * import { fromEvent } from 'rxjs';
  34. * import { debounceTime } from 'rxjs/operators';
  35. *
  36. * const clicks = fromEvent(document, 'click');
  37. * const result = clicks.pipe(debounceTime(1000));
  38. * result.subscribe(x => console.log(x));
  39. * ```
  40. *
  41. * @see {@link auditTime}
  42. * @see {@link debounce}
  43. * @see {@link delay}
  44. * @see {@link sampleTime}
  45. * @see {@link throttleTime}
  46. *
  47. * @param {number} dueTime The timeout duration in milliseconds (or the time
  48. * unit determined internally by the optional `scheduler`) for the window of
  49. * time required to wait for emission silence before emitting the most recent
  50. * source value.
  51. * @param {SchedulerLike} [scheduler=async] The {@link SchedulerLike} to use for
  52. * managing the timers that handle the timeout for each value.
  53. * @return {Observable} An Observable that delays the emissions of the source
  54. * Observable by the specified `dueTime`, and may drop some values if they occur
  55. * too frequently.
  56. * @method debounceTime
  57. * @owner Observable
  58. */
  59. export function debounceTime<T>(dueTime: number, scheduler: SchedulerLike = async): MonoTypeOperatorFunction<T> {
  60. return (source: Observable<T>) => source.lift(new DebounceTimeOperator(dueTime, scheduler));
  61. }
  62. class DebounceTimeOperator<T> implements Operator<T, T> {
  63. constructor(private dueTime: number, private scheduler: SchedulerLike) {
  64. }
  65. call(subscriber: Subscriber<T>, source: any): TeardownLogic {
  66. return source.subscribe(new DebounceTimeSubscriber(subscriber, this.dueTime, this.scheduler));
  67. }
  68. }
  69. /**
  70. * We need this JSDoc comment for affecting ESDoc.
  71. * @ignore
  72. * @extends {Ignored}
  73. */
  74. class DebounceTimeSubscriber<T> extends Subscriber<T> {
  75. private debouncedSubscription: Subscription = null;
  76. private lastValue: T = null;
  77. private hasValue: boolean = false;
  78. constructor(destination: Subscriber<T>,
  79. private dueTime: number,
  80. private scheduler: SchedulerLike) {
  81. super(destination);
  82. }
  83. protected _next(value: T) {
  84. this.clearDebounce();
  85. this.lastValue = value;
  86. this.hasValue = true;
  87. this.add(this.debouncedSubscription = this.scheduler.schedule(dispatchNext, this.dueTime, this));
  88. }
  89. protected _complete() {
  90. this.debouncedNext();
  91. this.destination.complete();
  92. }
  93. debouncedNext(): void {
  94. this.clearDebounce();
  95. if (this.hasValue) {
  96. const { lastValue } = this;
  97. // This must be done *before* passing the value
  98. // along to the destination because it's possible for
  99. // the value to synchronously re-enter this operator
  100. // recursively when scheduled with things like
  101. // VirtualScheduler/TestScheduler.
  102. this.lastValue = null;
  103. this.hasValue = false;
  104. this.destination.next(lastValue);
  105. }
  106. }
  107. private clearDebounce(): void {
  108. const debouncedSubscription = this.debouncedSubscription;
  109. if (debouncedSubscription !== null) {
  110. this.remove(debouncedSubscription);
  111. debouncedSubscription.unsubscribe();
  112. this.debouncedSubscription = null;
  113. }
  114. }
  115. }
  116. function dispatchNext(subscriber: DebounceTimeSubscriber<any>) {
  117. subscriber.debouncedNext();
  118. }