Repositorio del curso CCOM4030 el semestre B91 del proyecto Artesanías con el Instituto de Cultura

generate.ts 13KB

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