a7b5ce2d6b9db6431cb45945bb53fdedfc5b1f9c94e1ac88d68f8a4df04fda4576fa7ab2719772a34f8cfbb26ddbf54d5d33aa14c7366312e4543046cf1a86 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387
  1. import { Observable } from '../Observable';
  2. import { identity } from '../util/identity';
  3. import { ObservableInput, SchedulerLike } from '../types';
  4. import { isScheduler } from '../util/isScheduler';
  5. import { defer } from './defer';
  6. import { scheduleIterable } from '../scheduled/scheduleIterable';
  7. type ConditionFunc<S> = (state: S) => boolean;
  8. type IterateFunc<S> = (state: S) => S;
  9. type ResultFunc<S, T> = (state: S) => T;
  10. export interface GenerateBaseOptions<S> {
  11. /**
  12. * Initial state.
  13. */
  14. initialState: S;
  15. /**
  16. * Condition function that accepts state and returns boolean.
  17. * When it returns false, the generator stops.
  18. * If not specified, a generator never stops.
  19. */
  20. condition?: ConditionFunc<S>;
  21. /**
  22. * Iterate function that accepts state and returns new state.
  23. */
  24. iterate: IterateFunc<S>;
  25. /**
  26. * SchedulerLike to use for generation process.
  27. * By default, a generator starts immediately.
  28. */
  29. scheduler?: SchedulerLike;
  30. }
  31. export interface GenerateOptions<T, S> extends GenerateBaseOptions<S> {
  32. /**
  33. * Result selection function that accepts state and returns a value to emit.
  34. */
  35. resultSelector: ResultFunc<S, T>;
  36. }
  37. /**
  38. * Generates an observable sequence by running a state-driven loop
  39. * producing the sequence's elements, using the specified scheduler
  40. * to send out observer messages.
  41. *
  42. * ![](generate.png)
  43. *
  44. * ## Examples
  45. *
  46. * Produces sequence of numbers
  47. *
  48. * ```ts
  49. * import { generate } from 'rxjs';
  50. *
  51. * const result = generate(0, x => x < 3, x => x + 1, x => x);
  52. *
  53. * result.subscribe(x => console.log(x));
  54. *
  55. * // Logs:
  56. * // 0
  57. * // 1
  58. * // 2
  59. * ```
  60. *
  61. * Use `asapScheduler`
  62. *
  63. * ```ts
  64. * import { generate, asapScheduler } from 'rxjs';
  65. *
  66. * const result = generate(1, x => x < 5, x => x * 2, x => x + 1, asapScheduler);
  67. *
  68. * result.subscribe(x => console.log(x));
  69. *
  70. * // Logs:
  71. * // 2
  72. * // 3
  73. * // 5
  74. * ```
  75. *
  76. * @see {@link from}
  77. * @see {@link Observable}
  78. *
  79. * @param initialState Initial state.
  80. * @param condition Condition to terminate generation (upon returning false).
  81. * @param iterate Iteration step function.
  82. * @param resultSelector Selector function for results produced in the sequence.
  83. * @param scheduler A {@link SchedulerLike} on which to run the generator loop.
  84. * If not provided, defaults to emit immediately.
  85. * @returns The generated sequence.
  86. * @deprecated Instead of passing separate arguments, use the options argument.
  87. * Signatures taking separate arguments will be removed in v8.
  88. */
  89. export function generate<T, S>(
  90. initialState: S,
  91. condition: ConditionFunc<S>,
  92. iterate: IterateFunc<S>,
  93. resultSelector: ResultFunc<S, T>,
  94. scheduler?: SchedulerLike
  95. ): Observable<T>;
  96. /**
  97. * Generates an Observable by running a state-driven loop
  98. * that emits an element on each iteration.
  99. *
  100. * <span class="informal">Use it instead of nexting values in a for loop.</span>
  101. *
  102. * ![](generate.png)
  103. *
  104. * `generate` allows you to create a stream of values generated with a loop very similar to
  105. * a traditional for loop. The first argument of `generate` is a beginning value. The second argument
  106. * is a function that accepts this value and tests if some condition still holds. If it does,
  107. * then the loop continues, if not, it stops. The third value is a function which takes the
  108. * previously defined value and modifies it in some way on each iteration. Note how these three parameters
  109. * are direct equivalents of three expressions in a traditional for loop: the first expression
  110. * initializes some state (for example, a numeric index), the second tests if the loop can perform the next
  111. * iteration (for example, if the index is lower than 10) and the third states how the defined value
  112. * will be modified on every step (for example, the index will be incremented by one).
  113. *
  114. * Return value of a `generate` operator is an Observable that on each loop iteration
  115. * emits a value. First of all, the condition function is ran. If it returns true, then the Observable
  116. * emits the currently stored value (initial value at the first iteration) and finally updates
  117. * that value with iterate function. If at some point the condition returns false, then the Observable
  118. * completes at that moment.
  119. *
  120. * Optionally you can pass a fourth parameter to `generate` - a result selector function which allows you
  121. * to immediately map the value that would normally be emitted by an Observable.
  122. *
  123. * If you find three anonymous functions in `generate` call hard to read, you can provide
  124. * a single object to the operator instead where the object has the properties: `initialState`,
  125. * `condition`, `iterate` and `resultSelector`, which should have respective values that you
  126. * would normally pass to `generate`. `resultSelector` is still optional, but that form
  127. * of calling `generate` allows you to omit `condition` as well. If you omit it, that means
  128. * condition always holds, or in other words the resulting Observable will never complete.
  129. *
  130. * Both forms of `generate` can optionally accept a scheduler. In case of a multi-parameter call,
  131. * scheduler simply comes as a last argument (no matter if there is a `resultSelector`
  132. * function or not). In case of a single-parameter call, you can provide it as a
  133. * `scheduler` property on the object passed to the operator. In both cases, a scheduler decides when
  134. * the next iteration of the loop will happen and therefore when the next value will be emitted
  135. * by the Observable. For example, to ensure that each value is pushed to the Observer
  136. * on a separate task in the event loop, you could use the `async` scheduler. Note that
  137. * by default (when no scheduler is passed) values are simply emitted synchronously.
  138. *
  139. *
  140. * ## Examples
  141. *
  142. * Use with condition and iterate functions
  143. *
  144. * ```ts
  145. * import { generate } from 'rxjs';
  146. *
  147. * const result = generate(0, x => x < 3, x => x + 1);
  148. *
  149. * result.subscribe({
  150. * next: value => console.log(value),
  151. * complete: () => console.log('Complete!')
  152. * });
  153. *
  154. * // Logs:
  155. * // 0
  156. * // 1
  157. * // 2
  158. * // 'Complete!'
  159. * ```
  160. *
  161. * Use with condition, iterate and resultSelector functions
  162. *
  163. * ```ts
  164. * import { generate } from 'rxjs';
  165. *
  166. * const result = generate(0, x => x < 3, x => x + 1, x => x * 1000);
  167. *
  168. * result.subscribe({
  169. * next: value => console.log(value),
  170. * complete: () => console.log('Complete!')
  171. * });
  172. *
  173. * // Logs:
  174. * // 0
  175. * // 1000
  176. * // 2000
  177. * // 'Complete!'
  178. * ```
  179. *
  180. * Use with options object
  181. *
  182. * ```ts
  183. * import { generate } from 'rxjs';
  184. *
  185. * const result = generate({
  186. * initialState: 0,
  187. * condition(value) { return value < 3; },
  188. * iterate(value) { return value + 1; },
  189. * resultSelector(value) { return value * 1000; }
  190. * });
  191. *
  192. * result.subscribe({
  193. * next: value => console.log(value),
  194. * complete: () => console.log('Complete!')
  195. * });
  196. *
  197. * // Logs:
  198. * // 0
  199. * // 1000
  200. * // 2000
  201. * // 'Complete!'
  202. * ```
  203. *
  204. * Use options object without condition function
  205. *
  206. * ```ts
  207. * import { generate } from 'rxjs';
  208. *
  209. * const result = generate({
  210. * initialState: 0,
  211. * iterate(value) { return value + 1; },
  212. * resultSelector(value) { return value * 1000; }
  213. * });
  214. *
  215. * result.subscribe({
  216. * next: value => console.log(value),
  217. * complete: () => console.log('Complete!') // This will never run
  218. * });
  219. *
  220. * // Logs:
  221. * // 0
  222. * // 1000
  223. * // 2000
  224. * // 3000
  225. * // ...and never stops.
  226. * ```
  227. *
  228. * @see {@link from}
  229. *
  230. * @param initialState Initial state.
  231. * @param condition Condition to terminate generation (upon returning false).
  232. * @param iterate Iteration step function.
  233. * @param scheduler A {@link Scheduler} on which to run the generator loop. If not
  234. * provided, defaults to emitting immediately.
  235. * @return The generated sequence.
  236. * @deprecated Instead of passing separate arguments, use the options argument.
  237. * Signatures taking separate arguments will be removed in v8.
  238. */
  239. export function generate<S>(
  240. initialState: S,
  241. condition: ConditionFunc<S>,
  242. iterate: IterateFunc<S>,
  243. scheduler?: SchedulerLike
  244. ): Observable<S>;
  245. /**
  246. * Generates an observable sequence by running a state-driven loop
  247. * producing the sequence's elements, using the specified scheduler
  248. * to send out observer messages.
  249. * The overload accepts options object that might contain initial state, iterate,
  250. * condition and scheduler.
  251. *
  252. * ![](generate.png)
  253. *
  254. * ## Examples
  255. *
  256. * Use options object with condition function
  257. *
  258. * ```ts
  259. * import { generate } from 'rxjs';
  260. *
  261. * const result = generate({
  262. * initialState: 0,
  263. * condition: x => x < 3,
  264. * iterate: x => x + 1
  265. * });
  266. *
  267. * result.subscribe({
  268. * next: value => console.log(value),
  269. * complete: () => console.log('Complete!')
  270. * });
  271. *
  272. * // Logs:
  273. * // 0
  274. * // 1
  275. * // 2
  276. * // 'Complete!'
  277. * ```
  278. *
  279. * @see {@link from}
  280. * @see {@link Observable}
  281. *
  282. * @param options Object that must contain initialState, iterate and might contain condition and scheduler.
  283. * @returns The generated sequence.
  284. */
  285. export function generate<S>(options: GenerateBaseOptions<S>): Observable<S>;
  286. /**
  287. * Generates an observable sequence by running a state-driven loop
  288. * producing the sequence's elements, using the specified scheduler
  289. * to send out observer messages.
  290. * The overload accepts options object that might contain initial state, iterate,
  291. * condition, result selector and scheduler.
  292. *
  293. * ![](generate.png)
  294. *
  295. * ## Examples
  296. *
  297. * Use options object with condition and iterate function
  298. *
  299. * ```ts
  300. * import { generate } from 'rxjs';
  301. *
  302. * const result = generate({
  303. * initialState: 0,
  304. * condition: x => x < 3,
  305. * iterate: x => x + 1,
  306. * resultSelector: x => x
  307. * });
  308. *
  309. * result.subscribe({
  310. * next: value => console.log(value),
  311. * complete: () => console.log('Complete!')
  312. * });
  313. *
  314. * // Logs:
  315. * // 0
  316. * // 1
  317. * // 2
  318. * // 'Complete!'
  319. * ```
  320. *
  321. * @see {@link from}
  322. * @see {@link Observable}
  323. *
  324. * @param options Object that must contain initialState, iterate, resultSelector and might contain condition and scheduler.
  325. * @returns The generated sequence.
  326. */
  327. export function generate<T, S>(options: GenerateOptions<T, S>): Observable<T>;
  328. export function generate<T, S>(
  329. initialStateOrOptions: S | GenerateOptions<T, S>,
  330. condition?: ConditionFunc<S>,
  331. iterate?: IterateFunc<S>,
  332. resultSelectorOrScheduler?: ResultFunc<S, T> | SchedulerLike,
  333. scheduler?: SchedulerLike
  334. ): Observable<T> {
  335. let resultSelector: ResultFunc<S, T>;
  336. let initialState: S;
  337. // TODO: Remove this as we move away from deprecated signatures
  338. // and move towards a configuration object argument.
  339. if (arguments.length === 1) {
  340. // If we only have one argument, we can assume it is a configuration object.
  341. // Note that folks not using TypeScript may trip over this.
  342. ({
  343. initialState,
  344. condition,
  345. iterate,
  346. resultSelector = identity as ResultFunc<S, T>,
  347. scheduler,
  348. } = initialStateOrOptions as GenerateOptions<T, S>);
  349. } else {
  350. // Deprecated arguments path. Figure out what the user
  351. // passed and set it here.
  352. initialState = initialStateOrOptions as S;
  353. if (!resultSelectorOrScheduler || isScheduler(resultSelectorOrScheduler)) {
  354. resultSelector = identity as ResultFunc<S, T>;
  355. scheduler = resultSelectorOrScheduler as SchedulerLike;
  356. } else {
  357. resultSelector = resultSelectorOrScheduler as ResultFunc<S, T>;
  358. }
  359. }
  360. // The actual generator used to "generate" values.
  361. function* gen() {
  362. for (let state = initialState; !condition || condition(state); state = iterate!(state)) {
  363. yield resultSelector(state);
  364. }
  365. }
  366. // We use `defer` because we want to defer the creation of the iterator from the iterable.
  367. return defer(
  368. (scheduler
  369. ? // If a scheduler was provided, use `scheduleIterable` to ensure that iteration/generation
  370. // happens on the scheduler.
  371. () => scheduleIterable(gen(), scheduler!)
  372. : // Otherwise, if there's no scheduler, we can just use the generator function directly in
  373. // `defer` and executing it will return the generator (which is iterable).
  374. gen) as () => ObservableInput<T>
  375. );
  376. }