f808d5509909d3a6923256c58469b28571fd97b8f5bbffd24ff386128419d8c0da9d59828633bd16cf2650d9888ab417b6150d503583057e2e8031a463d5d5 4.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123
  1. import { asyncScheduler } from '../scheduler/async';
  2. import { Subscription } from '../Subscription';
  3. import { MonoTypeOperatorFunction, SchedulerAction, SchedulerLike } from '../types';
  4. import { operate } from '../util/lift';
  5. import { createOperatorSubscriber } from './OperatorSubscriber';
  6. /**
  7. * Emits a notification from the source Observable only after a particular time span
  8. * has passed without another source emission.
  9. *
  10. * <span class="informal">It's like {@link delay}, but passes only the most
  11. * recent notification from each burst of emissions.</span>
  12. *
  13. * ![](debounceTime.png)
  14. *
  15. * `debounceTime` delays notifications emitted by the source Observable, but drops
  16. * previous pending delayed emissions if a new notification arrives on the source
  17. * Observable. This operator keeps track of the most recent notification from the
  18. * source Observable, and emits that only when `dueTime` has passed
  19. * without any other notification appearing on the source Observable. If a new value
  20. * appears before `dueTime` silence occurs, the previous notification will be dropped
  21. * and will not be emitted and a new `dueTime` is scheduled.
  22. * If the completing event happens during `dueTime` the last cached notification
  23. * is emitted before the completion event is forwarded to the output observable.
  24. * If the error event happens during `dueTime` or after it only the error event is
  25. * forwarded to the output observable. The cache notification is not emitted in this case.
  26. *
  27. * This is a rate-limiting operator, because it is impossible for more than one
  28. * notification to be emitted in any time window of duration `dueTime`, but it is also
  29. * a delay-like operator since output emissions do not occur at the same time as
  30. * they did on the source Observable. Optionally takes a {@link SchedulerLike} for
  31. * managing timers.
  32. *
  33. * ## Example
  34. *
  35. * Emit the most recent click after a burst of clicks
  36. *
  37. * ```ts
  38. * import { fromEvent, debounceTime } from 'rxjs';
  39. *
  40. * const clicks = fromEvent(document, 'click');
  41. * const result = clicks.pipe(debounceTime(1000));
  42. * result.subscribe(x => console.log(x));
  43. * ```
  44. *
  45. * @see {@link audit}
  46. * @see {@link auditTime}
  47. * @see {@link debounce}
  48. * @see {@link sample}
  49. * @see {@link sampleTime}
  50. * @see {@link throttle}
  51. * @see {@link throttleTime}
  52. *
  53. * @param dueTime The timeout duration in milliseconds (or the time unit determined
  54. * internally by the optional `scheduler`) for the window of time required to wait
  55. * for emission silence before emitting the most recent source value.
  56. * @param scheduler The {@link SchedulerLike} to use for managing the timers that
  57. * handle the timeout for each value.
  58. * @return A function that returns an Observable that delays the emissions of
  59. * the source Observable by the specified `dueTime`, and may drop some values
  60. * if they occur too frequently.
  61. */
  62. export function debounceTime<T>(dueTime: number, scheduler: SchedulerLike = asyncScheduler): MonoTypeOperatorFunction<T> {
  63. return operate((source, subscriber) => {
  64. let activeTask: Subscription | null = null;
  65. let lastValue: T | null = null;
  66. let lastTime: number | null = null;
  67. const emit = () => {
  68. if (activeTask) {
  69. // We have a value! Free up memory first, then emit the value.
  70. activeTask.unsubscribe();
  71. activeTask = null;
  72. const value = lastValue!;
  73. lastValue = null;
  74. subscriber.next(value);
  75. }
  76. };
  77. function emitWhenIdle(this: SchedulerAction<unknown>) {
  78. // This is called `dueTime` after the first value
  79. // but we might have received new values during this window!
  80. const targetTime = lastTime! + dueTime;
  81. const now = scheduler.now();
  82. if (now < targetTime) {
  83. // On that case, re-schedule to the new target
  84. activeTask = this.schedule(undefined, targetTime - now);
  85. subscriber.add(activeTask);
  86. return;
  87. }
  88. emit();
  89. }
  90. source.subscribe(
  91. createOperatorSubscriber(
  92. subscriber,
  93. (value: T) => {
  94. lastValue = value;
  95. lastTime = scheduler.now();
  96. // Only set up a task if it's not already up
  97. if (!activeTask) {
  98. activeTask = scheduler.schedule(emitWhenIdle, dueTime);
  99. subscriber.add(activeTask);
  100. }
  101. },
  102. () => {
  103. // Source completed.
  104. // Emit any pending debounced values then complete
  105. emit();
  106. subscriber.complete();
  107. },
  108. // Pass all errors through to consumer.
  109. undefined,
  110. () => {
  111. // Finalization.
  112. lastValue = activeTask = null;
  113. }
  114. )
  115. );
  116. });
  117. }