Ohm-Management - Projektarbeit B-ME
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

windowTime.ts 9.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282
  1. import { Subject } from '../Subject';
  2. import { Operator } from '../Operator';
  3. import { async } from '../scheduler/async';
  4. import { Subscriber } from '../Subscriber';
  5. import { Observable } from '../Observable';
  6. import { Subscription } from '../Subscription';
  7. import { isNumeric } from '../util/isNumeric';
  8. import { isScheduler } from '../util/isScheduler';
  9. import { OperatorFunction, SchedulerLike, SchedulerAction } from '../types';
  10. /**
  11. * Branch out the source Observable values as a nested Observable periodically
  12. * in time.
  13. *
  14. * <span class="informal">It's like {@link bufferTime}, but emits a nested
  15. * Observable instead of an array.</span>
  16. *
  17. * ![](windowTime.png)
  18. *
  19. * Returns an Observable that emits windows of items it collects from the source
  20. * Observable. The output Observable starts a new window periodically, as
  21. * determined by the `windowCreationInterval` argument. It emits each window
  22. * after a fixed timespan, specified by the `windowTimeSpan` argument. When the
  23. * source Observable completes or encounters an error, the output Observable
  24. * emits the current window and propagates the notification from the source
  25. * Observable. If `windowCreationInterval` is not provided, the output
  26. * Observable starts a new window when the previous window of duration
  27. * `windowTimeSpan` completes. If `maxWindowCount` is provided, each window
  28. * will emit at most fixed number of values. Window will complete immediately
  29. * after emitting last value and next one still will open as specified by
  30. * `windowTimeSpan` and `windowCreationInterval` arguments.
  31. *
  32. * ## Examples
  33. * In every window of 1 second each, emit at most 2 click events
  34. * ```ts
  35. * import { fromEvent } from 'rxjs';
  36. * import { windowTime, map, mergeAll, take } from 'rxjs/operators';
  37. *
  38. * const clicks = fromEvent(document, 'click');
  39. * const result = clicks.pipe(
  40. * windowTime(1000),
  41. * map(win => win.pipe(take(2))), // each window has at most 2 emissions
  42. * mergeAll(), // flatten the Observable-of-Observables
  43. * );
  44. * result.subscribe(x => console.log(x));
  45. * ```
  46. *
  47. * Every 5 seconds start a window 1 second long, and emit at most 2 click events per window
  48. * ```ts
  49. * import { fromEvent } from 'rxjs';
  50. * import { windowTime, map, mergeAll, take } from 'rxjs/operators';
  51. *
  52. * const clicks = fromEvent(document, 'click');
  53. * const result = clicks.pipe(
  54. * windowTime(1000, 5000),
  55. * map(win => win.pipe(take(2))), // each window has at most 2 emissions
  56. * mergeAll(), // flatten the Observable-of-Observables
  57. * );
  58. * result.subscribe(x => console.log(x));
  59. * ```
  60. *
  61. * Same as example above but with maxWindowCount instead of take
  62. * ```ts
  63. * import { fromEvent } from 'rxjs';
  64. * import { windowTime, mergeAll } from 'rxjs/operators';
  65. *
  66. * const clicks = fromEvent(document, 'click');
  67. * const result = clicks.pipe(
  68. * windowTime(1000, 5000, 2), // each window has still at most 2 emissions
  69. * mergeAll(), // flatten the Observable-of-Observables
  70. * );
  71. * result.subscribe(x => console.log(x));
  72. * ```
  73. *
  74. * @see {@link window}
  75. * @see {@link windowCount}
  76. * @see {@link windowToggle}
  77. * @see {@link windowWhen}
  78. * @see {@link bufferTime}
  79. *
  80. * @param {number} windowTimeSpan The amount of time to fill each window.
  81. * @param {number} [windowCreationInterval] The interval at which to start new
  82. * windows.
  83. * @param {number} [maxWindowSize=Number.POSITIVE_INFINITY] Max number of
  84. * values each window can emit before completion.
  85. * @param {SchedulerLike} [scheduler=async] The scheduler on which to schedule the
  86. * intervals that determine window boundaries.
  87. * @return {Observable<Observable<T>>} An observable of windows, which in turn
  88. * are Observables.
  89. * @method windowTime
  90. * @owner Observable
  91. */
  92. export function windowTime<T>(windowTimeSpan: number,
  93. scheduler?: SchedulerLike): OperatorFunction<T, Observable<T>>;
  94. export function windowTime<T>(windowTimeSpan: number,
  95. windowCreationInterval: number,
  96. scheduler?: SchedulerLike): OperatorFunction<T, Observable<T>>;
  97. export function windowTime<T>(windowTimeSpan: number,
  98. windowCreationInterval: number,
  99. maxWindowSize: number,
  100. scheduler?: SchedulerLike): OperatorFunction<T, Observable<T>>;
  101. export function windowTime<T>(windowTimeSpan: number): OperatorFunction<T, Observable<T>> {
  102. let scheduler: SchedulerLike = async;
  103. let windowCreationInterval: number = null;
  104. let maxWindowSize: number = Number.POSITIVE_INFINITY;
  105. if (isScheduler(arguments[3])) {
  106. scheduler = arguments[3];
  107. }
  108. if (isScheduler(arguments[2])) {
  109. scheduler = arguments[2];
  110. } else if (isNumeric(arguments[2])) {
  111. maxWindowSize = arguments[2];
  112. }
  113. if (isScheduler(arguments[1])) {
  114. scheduler = arguments[1];
  115. } else if (isNumeric(arguments[1])) {
  116. windowCreationInterval = arguments[1];
  117. }
  118. return function windowTimeOperatorFunction(source: Observable<T>) {
  119. return source.lift(new WindowTimeOperator<T>(windowTimeSpan, windowCreationInterval, maxWindowSize, scheduler));
  120. };
  121. }
  122. class WindowTimeOperator<T> implements Operator<T, Observable<T>> {
  123. constructor(private windowTimeSpan: number,
  124. private windowCreationInterval: number | null,
  125. private maxWindowSize: number,
  126. private scheduler: SchedulerLike) {
  127. }
  128. call(subscriber: Subscriber<Observable<T>>, source: any): any {
  129. return source.subscribe(new WindowTimeSubscriber(
  130. subscriber, this.windowTimeSpan, this.windowCreationInterval, this.maxWindowSize, this.scheduler
  131. ));
  132. }
  133. }
  134. interface CreationState<T> {
  135. windowTimeSpan: number;
  136. windowCreationInterval: number;
  137. subscriber: WindowTimeSubscriber<T>;
  138. scheduler: SchedulerLike;
  139. }
  140. interface TimeSpanOnlyState<T> {
  141. window: CountedSubject<T>;
  142. windowTimeSpan: number;
  143. subscriber: WindowTimeSubscriber<T>;
  144. }
  145. interface CloseWindowContext<T> {
  146. action: SchedulerAction<CreationState<T>>;
  147. subscription: Subscription;
  148. }
  149. interface CloseState<T> {
  150. subscriber: WindowTimeSubscriber<T>;
  151. window: CountedSubject<T>;
  152. context: CloseWindowContext<T>;
  153. }
  154. class CountedSubject<T> extends Subject<T> {
  155. private _numberOfNextedValues: number = 0;
  156. next(value?: T): void {
  157. this._numberOfNextedValues++;
  158. super.next(value);
  159. }
  160. get numberOfNextedValues(): number {
  161. return this._numberOfNextedValues;
  162. }
  163. }
  164. /**
  165. * We need this JSDoc comment for affecting ESDoc.
  166. * @ignore
  167. * @extends {Ignored}
  168. */
  169. class WindowTimeSubscriber<T> extends Subscriber<T> {
  170. private windows: CountedSubject<T>[] = [];
  171. constructor(protected destination: Subscriber<Observable<T>>,
  172. private windowTimeSpan: number,
  173. private windowCreationInterval: number | null,
  174. private maxWindowSize: number,
  175. private scheduler: SchedulerLike) {
  176. super(destination);
  177. const window = this.openWindow();
  178. if (windowCreationInterval !== null && windowCreationInterval >= 0) {
  179. const closeState: CloseState<T> = { subscriber: this, window, context: <any>null };
  180. const creationState: CreationState<T> = { windowTimeSpan, windowCreationInterval, subscriber: this, scheduler };
  181. this.add(scheduler.schedule<CloseState<T>>(dispatchWindowClose, windowTimeSpan, closeState));
  182. this.add(scheduler.schedule<CreationState<T>>(dispatchWindowCreation, windowCreationInterval, creationState));
  183. } else {
  184. const timeSpanOnlyState: TimeSpanOnlyState<T> = { subscriber: this, window, windowTimeSpan };
  185. this.add(scheduler.schedule<TimeSpanOnlyState<T>>(dispatchWindowTimeSpanOnly, windowTimeSpan, timeSpanOnlyState));
  186. }
  187. }
  188. protected _next(value: T): void {
  189. const windows = this.windows;
  190. const len = windows.length;
  191. for (let i = 0; i < len; i++) {
  192. const window = windows[i];
  193. if (!window.closed) {
  194. window.next(value);
  195. if (window.numberOfNextedValues >= this.maxWindowSize) {
  196. this.closeWindow(window);
  197. }
  198. }
  199. }
  200. }
  201. protected _error(err: any): void {
  202. const windows = this.windows;
  203. while (windows.length > 0) {
  204. windows.shift().error(err);
  205. }
  206. this.destination.error(err);
  207. }
  208. protected _complete(): void {
  209. const windows = this.windows;
  210. while (windows.length > 0) {
  211. const window = windows.shift();
  212. if (!window.closed) {
  213. window.complete();
  214. }
  215. }
  216. this.destination.complete();
  217. }
  218. public openWindow(): CountedSubject<T> {
  219. const window = new CountedSubject<T>();
  220. this.windows.push(window);
  221. const destination = this.destination;
  222. destination.next(window);
  223. return window;
  224. }
  225. public closeWindow(window: CountedSubject<T>): void {
  226. window.complete();
  227. const windows = this.windows;
  228. windows.splice(windows.indexOf(window), 1);
  229. }
  230. }
  231. function dispatchWindowTimeSpanOnly<T>(this: SchedulerAction<TimeSpanOnlyState<T>>, state: TimeSpanOnlyState<T>): void {
  232. const { subscriber, windowTimeSpan, window } = state;
  233. if (window) {
  234. subscriber.closeWindow(window);
  235. }
  236. state.window = subscriber.openWindow();
  237. this.schedule(state, windowTimeSpan);
  238. }
  239. function dispatchWindowCreation<T>(this: SchedulerAction<CreationState<T>>, state: CreationState<T>): void {
  240. const { windowTimeSpan, subscriber, scheduler, windowCreationInterval } = state;
  241. const window = subscriber.openWindow();
  242. const action = this;
  243. let context: CloseWindowContext<T> = { action, subscription: <any>null };
  244. const timeSpanState: CloseState<T> = { subscriber, window, context };
  245. context.subscription = scheduler.schedule<CloseState<T>>(dispatchWindowClose, windowTimeSpan, timeSpanState);
  246. action.add(context.subscription);
  247. action.schedule(state, windowCreationInterval);
  248. }
  249. function dispatchWindowClose<T>(state: CloseState<T>): void {
  250. const { subscriber, window, context } = state;
  251. if (context && context.action && context.subscription) {
  252. context.action.remove(context.subscription);
  253. }
  254. subscriber.closeWindow(window);
  255. }