85ce0bdd7487e9cf22cad6bbd5affc1013132fbcb6924067040987a4ab4c3d9864a918a0ced53ba25efea87812e68b503875c430d3bca16020723f55c86a7e 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. import { Observable } from '../Observable';
  2. import { SchedulerLike } from '../types';
  3. import { async as asyncScheduler } from '../scheduler/async';
  4. import { isScheduler } from '../util/isScheduler';
  5. import { isValidDate } from '../util/isDate';
  6. /**
  7. * Creates an observable that will wait for a specified time period, or exact date, before
  8. * emitting the number 0.
  9. *
  10. * <span class="informal">Used to emit a notification after a delay.</span>
  11. *
  12. * This observable is useful for creating delays in code, or racing against other values
  13. * for ad-hoc timeouts.
  14. *
  15. * The `delay` is specified by default in milliseconds, however providing a custom scheduler could
  16. * create a different behavior.
  17. *
  18. * ## Examples
  19. *
  20. * Wait 3 seconds and start another observable
  21. *
  22. * You might want to use `timer` to delay subscription to an
  23. * observable by a set amount of time. Here we use a timer with
  24. * {@link concatMapTo} or {@link concatMap} in order to wait
  25. * a few seconds and start a subscription to a source.
  26. *
  27. * ```ts
  28. * import { of, timer, concatMap } from 'rxjs';
  29. *
  30. * // This could be any observable
  31. * const source = of(1, 2, 3);
  32. *
  33. * timer(3000)
  34. * .pipe(concatMap(() => source))
  35. * .subscribe(console.log);
  36. * ```
  37. *
  38. * Take all values until the start of the next minute
  39. *
  40. * Using a `Date` as the trigger for the first emission, you can
  41. * do things like wait until midnight to fire an event, or in this case,
  42. * wait until a new minute starts (chosen so the example wouldn't take
  43. * too long to run) in order to stop watching a stream. Leveraging
  44. * {@link takeUntil}.
  45. *
  46. * ```ts
  47. * import { interval, takeUntil, timer } from 'rxjs';
  48. *
  49. * // Build a Date object that marks the
  50. * // next minute.
  51. * const currentDate = new Date();
  52. * const startOfNextMinute = new Date(
  53. * currentDate.getFullYear(),
  54. * currentDate.getMonth(),
  55. * currentDate.getDate(),
  56. * currentDate.getHours(),
  57. * currentDate.getMinutes() + 1
  58. * );
  59. *
  60. * // This could be any observable stream
  61. * const source = interval(1000);
  62. *
  63. * const result = source.pipe(
  64. * takeUntil(timer(startOfNextMinute))
  65. * );
  66. *
  67. * result.subscribe(console.log);
  68. * ```
  69. *
  70. * ### Known Limitations
  71. *
  72. * - The {@link asyncScheduler} uses `setTimeout` which has limitations for how far in the future it can be scheduled.
  73. *
  74. * - If a `scheduler` is provided that returns a timestamp other than an epoch from `now()`, and
  75. * a `Date` object is passed to the `dueTime` argument, the calculation for when the first emission
  76. * should occur will be incorrect. In this case, it would be best to do your own calculations
  77. * ahead of time, and pass a `number` in as the `dueTime`.
  78. *
  79. * @param due If a `number`, the amount of time in milliseconds to wait before emitting.
  80. * If a `Date`, the exact time at which to emit.
  81. * @param scheduler The scheduler to use to schedule the delay. Defaults to {@link asyncScheduler}.
  82. */
  83. export function timer(due: number | Date, scheduler?: SchedulerLike): Observable<0>;
  84. /**
  85. * Creates an observable that starts an interval after a specified delay, emitting incrementing numbers -- starting at `0` --
  86. * on each interval after words.
  87. *
  88. * The `delay` and `intervalDuration` are specified by default in milliseconds, however providing a custom scheduler could
  89. * create a different behavior.
  90. *
  91. * ## Example
  92. *
  93. * ### Start an interval that starts right away
  94. *
  95. * Since {@link interval} waits for the passed delay before starting,
  96. * sometimes that's not ideal. You may want to start an interval immediately.
  97. * `timer` works well for this. Here we have both side-by-side so you can
  98. * see them in comparison.
  99. *
  100. * Note that this observable will never complete.
  101. *
  102. * ```ts
  103. * import { timer, interval } from 'rxjs';
  104. *
  105. * timer(0, 1000).subscribe(n => console.log('timer', n));
  106. * interval(1000).subscribe(n => console.log('interval', n));
  107. * ```
  108. *
  109. * ### Known Limitations
  110. *
  111. * - The {@link asyncScheduler} uses `setTimeout` which has limitations for how far in the future it can be scheduled.
  112. *
  113. * - If a `scheduler` is provided that returns a timestamp other than an epoch from `now()`, and
  114. * a `Date` object is passed to the `dueTime` argument, the calculation for when the first emission
  115. * should occur will be incorrect. In this case, it would be best to do your own calculations
  116. * ahead of time, and pass a `number` in as the `startDue`.
  117. * @param startDue If a `number`, is the time to wait before starting the interval.
  118. * If a `Date`, is the exact time at which to start the interval.
  119. * @param intervalDuration The delay between each value emitted in the interval. Passing a
  120. * negative number here will result in immediate completion after the first value is emitted, as though
  121. * no `intervalDuration` was passed at all.
  122. * @param scheduler The scheduler to use to schedule the delay. Defaults to {@link asyncScheduler}.
  123. */
  124. export function timer(startDue: number | Date, intervalDuration: number, scheduler?: SchedulerLike): Observable<number>;
  125. /**
  126. * @deprecated The signature allowing `undefined` to be passed for `intervalDuration` will be removed in v8. Use the `timer(dueTime, scheduler?)` signature instead.
  127. */
  128. export function timer(dueTime: number | Date, unused: undefined, scheduler?: SchedulerLike): Observable<0>;
  129. export function timer(
  130. dueTime: number | Date = 0,
  131. intervalOrScheduler?: number | SchedulerLike,
  132. scheduler: SchedulerLike = asyncScheduler
  133. ): Observable<number> {
  134. // Since negative intervalDuration is treated as though no
  135. // interval was specified at all, we start with a negative number.
  136. let intervalDuration = -1;
  137. if (intervalOrScheduler != null) {
  138. // If we have a second argument, and it's a scheduler,
  139. // override the scheduler we had defaulted. Otherwise,
  140. // it must be an interval.
  141. if (isScheduler(intervalOrScheduler)) {
  142. scheduler = intervalOrScheduler;
  143. } else {
  144. // Note that this *could* be negative, in which case
  145. // it's like not passing an intervalDuration at all.
  146. intervalDuration = intervalOrScheduler;
  147. }
  148. }
  149. return new Observable((subscriber) => {
  150. // If a valid date is passed, calculate how long to wait before
  151. // executing the first value... otherwise, if it's a number just schedule
  152. // that many milliseconds (or scheduler-specified unit size) in the future.
  153. let due = isValidDate(dueTime) ? +dueTime - scheduler!.now() : dueTime;
  154. if (due < 0) {
  155. // Ensure we don't schedule in the future.
  156. due = 0;
  157. }
  158. // The incrementing value we emit.
  159. let n = 0;
  160. // Start the timer.
  161. return scheduler.schedule(function () {
  162. if (!subscriber.closed) {
  163. // Emit the next value and increment.
  164. subscriber.next(n++);
  165. if (0 <= intervalDuration) {
  166. // If we have a interval after the initial timer,
  167. // reschedule with the period.
  168. this.schedule(undefined, intervalDuration);
  169. } else {
  170. // We didn't have an interval. So just complete.
  171. subscriber.complete();
  172. }
  173. }
  174. }, due);
  175. });
  176. }