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