@quenty/rx 7.10.0 → 7.11.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -3,6 +3,17 @@
3
3
  All notable changes to this project will be documented in this file.
4
4
  See [Conventional Commits](https://conventionalcommits.org) for commit guidelines.
5
5
 
6
+ # [7.11.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/rx@7.10.0...@quenty/rx@7.11.0) (2023-05-26)
7
+
8
+
9
+ ### Features
10
+
11
+ * Add Rx.scan, Rx.reduce, and Rx.throttle ([995a435](https://github.com/Quenty/NevermoreEngine/commit/995a43579ec836c0a300013a2326740f2b14b1d8))
12
+
13
+
14
+
15
+
16
+
6
17
  # [7.10.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/rx@7.9.1...@quenty/rx@7.10.0) (2023-04-10)
7
18
 
8
19
  **Note:** Version bump only for package @quenty/rx
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quenty/rx",
3
- "version": "7.10.0",
3
+ "version": "7.11.0",
4
4
  "description": "Quenty's reactive library for Roblox",
5
5
  "keywords": [
6
6
  "Roblox",
@@ -28,11 +28,11 @@
28
28
  ],
29
29
  "dependencies": {
30
30
  "@quenty/cancellabledelay": "^3.4.0",
31
- "@quenty/canceltoken": "^6.5.0",
31
+ "@quenty/canceltoken": "^6.6.0",
32
32
  "@quenty/loader": "^6.2.1",
33
33
  "@quenty/maid": "^2.5.0",
34
34
  "@quenty/promise": "^6.5.0",
35
- "@quenty/signal": "^2.3.0",
35
+ "@quenty/signal": "^2.4.0",
36
36
  "@quenty/symbol": "^2.2.0",
37
37
  "@quenty/table": "^3.2.0",
38
38
  "@quenty/throttle": "^6.2.1"
@@ -40,5 +40,5 @@
40
40
  "publishConfig": {
41
41
  "access": "public"
42
42
  },
43
- "gitHead": "3306212248c310731931ad45d8b86dc7247f2a5d"
43
+ "gitHead": "11058e90e51ea83d3dad6ae9abe59cc19c36b94b"
44
44
  }
package/src/Shared/Rx.lua CHANGED
@@ -575,6 +575,81 @@ function Rx.startWith(values)
575
575
  end
576
576
  end
577
577
 
578
+ --[=[
579
+ The Scan operator applies a function to the first item emitted by the source Observable and then
580
+ emits the result of that function as its own first emission. It also feeds the result of the function
581
+ back into the function along with the second item emitted by the source Observable in order to generate
582
+ its second emission. It continues to feed back its own subsequent emissions along with the subsequent
583
+ emissions from the source Observable in order to create the rest of its sequence.
584
+
585
+ https://reactivex.io/documentation/operators/scan.html
586
+
587
+ @param reducer function
588
+ @param seed any | nil
589
+ @return (source: Observable) -> Observable
590
+ ]=]
591
+ function Rx.scan(reducer, seed)
592
+ assert(type(reducer) == "function", "Bad reducer")
593
+
594
+ return function(source)
595
+ assert(Observable.isObservable(source), "Bad observable")
596
+
597
+ return Observable.new(function(sub)
598
+ local maid = Maid.new()
599
+ local current = seed
600
+
601
+ maid:GiveTask(source:Subscribe(
602
+ function(...)
603
+ current = reducer(current, ...)
604
+ sub:Fire(current)
605
+ end,
606
+ sub:GetFailComplete()))
607
+
608
+ return maid
609
+ end)
610
+ end
611
+ end
612
+
613
+ --[=[
614
+ The Reduce operator applies a function to the first item emitted by the source Observable and
615
+ then feeds the result of the function back into the function along with the second item emitted
616
+ by the source Observable, continuing this process until the source Observable emits its final
617
+ item and completes, whereupon the Observable returned from Reduce emits the final value returned
618
+ from the function.
619
+
620
+ https://reactivex.io/documentation/operators/reduce.html
621
+
622
+ @param reducer function
623
+ @param seed any | nil
624
+ @return (source: Observable) -> Observable
625
+ ]=]
626
+ function Rx.reduce(reducer, seed)
627
+ assert(type(reducer) == "function", "Bad reducer")
628
+
629
+ return function(source)
630
+ assert(Observable.isObservable(source), "Bad observable")
631
+
632
+ return Observable.new(function(sub)
633
+ local maid = Maid.new()
634
+ local current = seed
635
+
636
+ maid:GiveTask(source:Subscribe(
637
+ function(...)
638
+ current = reducer(current, ...)
639
+ end,
640
+ function(...)
641
+ sub:Fail(...)
642
+ end),
643
+ function()
644
+ -- On complete emit the result.
645
+ sub:Fire(current)
646
+ end)
647
+
648
+ return maid
649
+ end)
650
+ end
651
+ end
652
+
578
653
  --[=[
579
654
  Defaults the observable to a value if it isn't fired immediately
580
655
 
@@ -1010,6 +1085,62 @@ function Rx.flatMap(project, resultSelector)
1010
1085
  end
1011
1086
  end
1012
1087
 
1088
+ --[=[
1089
+ Switches to a new observable from the current observable
1090
+
1091
+ https://rxjs.dev/api/operators/switchMap
1092
+
1093
+ As each observable shows up, a new observable is mapped from that observable.
1094
+
1095
+ The old observable is disconnected.
1096
+
1097
+ Use Rx.switchMap to switch to a new RunService event
1098
+
1099
+ ```lua
1100
+ Rx.of(1, 2, 3):Pipe({
1101
+ Rx.switchMap(function(value)
1102
+ local startTime = os.clock()
1103
+
1104
+ -- Only the last observable returned will continue to emit,
1105
+ -- others are disconnected.
1106
+ return Rx.of(RunService.RenderStepped):Pipe({
1107
+ Rx.map(function()
1108
+ return os.clock() - startTime, value
1109
+ end);
1110
+ });
1111
+ end);
1112
+ }):Subscribe(print) --> 0.002352342, 3
1113
+ ```
1114
+
1115
+ Use Rx.switchMap() as a simple map...
1116
+
1117
+ ```lua
1118
+ Rx.of(1, 2, 3):Pipe({
1119
+ Rx.switchMap(function(value)
1120
+ print(value) --> 1 (and then 2, and then 3)
1121
+
1122
+ return Rx.of(value*2)
1123
+ end);
1124
+ }):Subscribe(print) --> 2, 4, 6
1125
+
1126
+ ```
1127
+
1128
+ Use Rx.switchMap() with delayed input (to swap to a new one)
1129
+
1130
+ ```lua
1131
+ Rx.of(1, 2, 3):Pipe({
1132
+ Rx.switchMap(function(value)
1133
+ -- Emit 1 second later
1134
+ return Rx.of(value*2):Pipe({
1135
+ Rx.delay(1); -- These will each get cancelled
1136
+ })
1137
+ end);
1138
+ }):Subscribe(print) --> 6 (other results were cancelled)
1139
+ ```
1140
+
1141
+ @param project function
1142
+ @return Observable
1143
+ ]=]
1013
1144
  function Rx.switchMap(project)
1014
1145
  return Rx.pipe({
1015
1146
  Rx.map(project);
@@ -1684,6 +1815,7 @@ function Rx.throttleDefer()
1684
1815
  end
1685
1816
  end)
1686
1817
  else
1818
+
1687
1819
  lastResult = table.pack(...)
1688
1820
  end
1689
1821
  end, sub:GetFailComplete()))
@@ -1693,4 +1825,52 @@ function Rx.throttleDefer()
1693
1825
  end
1694
1826
  end
1695
1827
 
1828
+ --[=[
1829
+ Throttles emission of observables on the defer stack to the last emission.
1830
+
1831
+ https://rxjs.dev/api/operators/throttle
1832
+
1833
+ @param durationSelector (T: value) -> Observable
1834
+ @return (source: Observable<T>) -> Observable<T>
1835
+ ]=]
1836
+ function Rx.throttle(durationSelector)
1837
+ return function(source)
1838
+ assert(Observable.isObservable(source), "Bad observable")
1839
+
1840
+ return Observable.new(function(sub)
1841
+ local topMaid = Maid.new()
1842
+
1843
+ local lastResult
1844
+
1845
+ topMaid:GiveTask(source:Subscribe(function(...)
1846
+ if not lastResult then
1847
+ lastResult = table.pack(...)
1848
+
1849
+ -- Queue up our result
1850
+ local maid = Maid.new()
1851
+
1852
+ maid:GiveTask(durationSelector(lastResult):Subscribe(function()
1853
+ local current = lastResult
1854
+ lastResult = nil
1855
+
1856
+ if sub:IsPending() then
1857
+ sub:Fire(table.unpack(current, 1, current.n))
1858
+ end
1859
+
1860
+ if topMaid._currentQueue == maid then
1861
+ topMaid._currentQueue = nil
1862
+ end
1863
+ end))
1864
+
1865
+ topMaid._currentQueue = maid
1866
+ else
1867
+ lastResult = table.pack(...)
1868
+ end
1869
+ end, sub:GetFailComplete()))
1870
+
1871
+ return topMaid
1872
+ end)
1873
+ end
1874
+ end
1875
+
1696
1876
  return Rx