@quenty/rx 13.7.0 → 13.8.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,19 @@
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
+ # [13.8.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/rx@13.7.0...@quenty/rx@13.8.0) (2024-10-04)
7
+
8
+
9
+ ### Bug Fixes
10
+
11
+ * Allow cleanup to be done in a safer way if the object is already cleaned up ([20b3ef1](https://github.com/Quenty/NevermoreEngine/commit/20b3ef1802e4bf92d0f46de99f3ec85660f17002))
12
+ * Do not fire observables that are cancelled ([2098b28](https://github.com/Quenty/NevermoreEngine/commit/2098b2809f40c9a813200b4f2789340901c6baee))
13
+ * Use nil instead of empty string for source here to save a bit of memory ([f1edd7f](https://github.com/Quenty/NevermoreEngine/commit/f1edd7f823ec0307517f1cba2717de147445608f))
14
+
15
+
16
+
17
+
18
+
6
19
  # [13.7.0](https://github.com/Quenty/NevermoreEngine/compare/@quenty/rx@13.6.0...@quenty/rx@13.7.0) (2024-09-25)
7
20
 
8
21
  **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": "13.7.0",
3
+ "version": "13.8.0",
4
4
  "description": "Quenty's reactive library for Roblox",
5
5
  "keywords": [
6
6
  "Roblox",
@@ -28,18 +28,18 @@
28
28
  ],
29
29
  "dependencies": {
30
30
  "@quenty/cancellabledelay": "^3.5.0",
31
- "@quenty/canceltoken": "^11.6.0",
32
- "@quenty/ducktype": "^5.5.0",
33
- "@quenty/loader": "^10.5.0",
34
- "@quenty/maid": "^3.3.0",
35
- "@quenty/promise": "^10.5.0",
36
- "@quenty/signal": "^7.6.0",
37
- "@quenty/symbol": "^3.1.0",
31
+ "@quenty/canceltoken": "^11.7.0",
32
+ "@quenty/ducktype": "^5.6.0",
33
+ "@quenty/loader": "^10.6.0",
34
+ "@quenty/maid": "^3.4.0",
35
+ "@quenty/promise": "^10.6.0",
36
+ "@quenty/signal": "^7.7.0",
37
+ "@quenty/symbol": "^3.2.0",
38
38
  "@quenty/table": "^3.5.0",
39
- "@quenty/throttle": "^10.6.0"
39
+ "@quenty/throttle": "^10.7.0"
40
40
  },
41
41
  "publishConfig": {
42
42
  "access": "public"
43
43
  },
44
- "gitHead": "9b17fe79cddd071f0f06a9d35184e76b44bd6fe6"
44
+ "gitHead": "035abfa088c854a73e1c65b350267eaa17669646"
45
45
  }
@@ -107,7 +107,7 @@ function Observable.new(onSubscribe)
107
107
  assert(type(onSubscribe) == "function", "Bad onSubscribe")
108
108
 
109
109
  return setmetatable({
110
- _source = ENABLE_STACK_TRACING and debug.traceback() or "";
110
+ _source = if ENABLE_STACK_TRACING then debug.traceback() else nil;
111
111
  _onSubscribe = onSubscribe;
112
112
  }, Observable)
113
113
  end
@@ -35,7 +35,9 @@ function ObservableSubscriptionTable:Fire(key, ...)
35
35
 
36
36
  -- Make a copy so we don't have to worry about our last changing
37
37
  for _, sub in pairs(table.clone(subs)) do
38
- task.spawn(sub.Fire, sub, ...)
38
+ if sub:IsPending() then
39
+ task.spawn(sub.Fire, sub, ...)
40
+ end
39
41
  end
40
42
  end
41
43
 
@@ -49,7 +51,9 @@ function ObservableSubscriptionTable:Complete(key, ...)
49
51
  self._subMap[key] = nil
50
52
 
51
53
  for _, sub in pairs(subsToComplete) do
52
- task.spawn(sub.Complete, sub, ...)
54
+ if sub:IsPending() then
55
+ task.spawn(sub.Complete, sub, ...)
56
+ end
53
57
  end
54
58
  end
55
59
 
@@ -74,11 +78,11 @@ function ObservableSubscriptionTable:Observe(key, retrieveInitialValue)
74
78
  end
75
79
 
76
80
  return function()
77
- if not self._subMap[key] then
81
+ local current = self._subMap[key]
82
+ if not current then
78
83
  return
79
84
  end
80
85
 
81
- local current = self._subMap[key]
82
86
  local index = table.find(current, sub)
83
87
  if not index then
84
88
  return
package/src/Shared/Rx.lua CHANGED
@@ -1444,20 +1444,19 @@ end
1444
1444
  function Rx.combineLatest(observables)
1445
1445
  assert(type(observables) == "table", "Bad observables")
1446
1446
 
1447
- local initialPending = 0
1448
- local defaultLatest = {}
1449
- for key, value in pairs(observables) do
1450
- if Observable.isObservable(value) then
1451
- initialPending = initialPending + 1
1452
- defaultLatest[key] = UNSET_VALUE
1453
- else
1454
- defaultLatest[key] = value
1455
- end
1456
- end
1457
-
1458
1447
  return Observable.new(function(sub)
1459
- local pending = initialPending
1460
- local latest = table.clone(defaultLatest)
1448
+ local pending = 0
1449
+ local latest = {}
1450
+
1451
+ -- Instead of caching this, use extra compute here
1452
+ for key, value in pairs(observables) do
1453
+ if Observable.isObservable(value) then
1454
+ pending = pending + 1
1455
+ latest[key] = UNSET_VALUE
1456
+ else
1457
+ latest[key] = value
1458
+ end
1459
+ end
1461
1460
 
1462
1461
  if pending == 0 then
1463
1462
  sub:Fire(latest)
@@ -1474,7 +1473,19 @@ function Rx.combineLatest(observables)
1474
1473
  end
1475
1474
  end
1476
1475
 
1477
- sub:Fire(table.clone(latest))
1476
+ sub:Fire(table.freeze(table.clone(latest)))
1477
+ end
1478
+
1479
+ local function failOnFirst(...)
1480
+ pending = pending - 1
1481
+ sub:Fail(...)
1482
+ end
1483
+
1484
+ local function completeOnAllPendingDone()
1485
+ pending = pending - 1
1486
+ if pending == 0 then
1487
+ sub:Complete()
1488
+ end
1478
1489
  end
1479
1490
 
1480
1491
  for key, observer in pairs(observables) do
@@ -1484,16 +1495,8 @@ function Rx.combineLatest(observables)
1484
1495
  latest[key] = value
1485
1496
  fireIfAllSet()
1486
1497
  end,
1487
- function(...)
1488
- pending = pending - 1
1489
- sub:Fail(...)
1490
- end,
1491
- function()
1492
- pending = pending - 1
1493
- if pending == 0 then
1494
- sub:Complete()
1495
- end
1496
- end))
1498
+ failOnFirst,
1499
+ completeOnAllPendingDone))
1497
1500
  end
1498
1501
  end
1499
1502
 
@@ -1859,30 +1862,37 @@ function Rx.throttleDefer()
1859
1862
  assert(Observable.isObservable(source), "Bad observable")
1860
1863
 
1861
1864
  return Observable.new(function(sub)
1862
- local maid = Maid.new()
1865
+ local lastResult = nil
1866
+ local currentQueue = nil
1863
1867
 
1864
- local lastResult
1865
-
1866
- maid:GiveTask(source:Subscribe(function(...)
1867
- if not lastResult then
1868
+ local sourceSub = source:Subscribe(function(...)
1869
+ if lastResult then
1868
1870
  lastResult = table.pack(...)
1871
+ return
1872
+ end
1869
1873
 
1870
- -- Queue up our result
1871
- maid._currentQueue = task.defer(function()
1872
- local current = lastResult
1873
- lastResult = nil
1874
+ lastResult = table.pack(...)
1874
1875
 
1875
- if sub:IsPending() then
1876
- sub:Fire(table.unpack(current, 1, current.n))
1877
- end
1878
- end)
1879
- else
1876
+ -- Queue up our result
1877
+ currentQueue = task.defer(function()
1878
+ local current = lastResult
1879
+ lastResult = nil
1880
+ currentQueue = nil
1880
1881
 
1881
- lastResult = table.pack(...)
1882
+ if sub:IsPending() then
1883
+ sub:Fire(table.unpack(current, 1, current.n))
1884
+ end
1885
+ end)
1886
+ end, sub:GetFailComplete())
1887
+
1888
+ return function()
1889
+ if currentQueue then
1890
+ task.cancel(currentQueue)
1891
+ currentQueue = nil
1882
1892
  end
1883
- end, sub:GetFailComplete()))
1884
1893
 
1885
- return maid
1894
+ sourceSub:Destroy()
1895
+ end
1886
1896
  end)
1887
1897
  end
1888
1898
  end
@@ -24,7 +24,7 @@ local Subscription = {}
24
24
  Subscription.ClassName = "Subscription"
25
25
  Subscription.__index = Subscription
26
26
 
27
- local stateTypes = {
27
+ local SubscriptionStateTypes = {
28
28
  PENDING = "pending";
29
29
  FAILED = "failed";
30
30
  COMPLETE = "complete";
@@ -46,8 +46,8 @@ function Subscription.new(fireCallback, failCallback, completeCallback, observab
46
46
  assert(type(completeCallback) == "function" or completeCallback == nil, "Bad completeCallback")
47
47
 
48
48
  return setmetatable({
49
- _state = stateTypes.PENDING;
50
- _source = ENABLE_STACK_TRACING and debug.traceback() or nil;
49
+ _state = SubscriptionStateTypes.PENDING;
50
+ _source = if ENABLE_STACK_TRACING then debug.traceback() else nil;
51
51
  _observableSource = observableSource;
52
52
  _fireCallback = fireCallback;
53
53
  _failCallback = failCallback;
@@ -61,11 +61,11 @@ end
61
61
  @param ... any
62
62
  ]=]
63
63
  function Subscription:Fire(...)
64
- if self._state == stateTypes.PENDING then
64
+ if self._state == SubscriptionStateTypes.PENDING then
65
65
  if self._fireCallback then
66
66
  self._fireCallback(...)
67
67
  end
68
- elseif self._state == stateTypes.CANCELLED then
68
+ elseif self._state == SubscriptionStateTypes.CANCELLED then
69
69
  warn("[Subscription.Fire] - We are cancelled, but events are still being pushed")
70
70
 
71
71
  if ENABLE_STACK_TRACING then
@@ -80,11 +80,11 @@ end
80
80
  Fails the subscription, preventing anything else from emitting.
81
81
  ]=]
82
82
  function Subscription:Fail()
83
- if self._state ~= stateTypes.PENDING then
83
+ if self._state ~= SubscriptionStateTypes.PENDING then
84
84
  return
85
85
  end
86
86
 
87
- self._state = stateTypes.FAILED
87
+ self._state = SubscriptionStateTypes.FAILED
88
88
 
89
89
  if self._failCallback then
90
90
  self._failCallback()
@@ -152,11 +152,11 @@ end
152
152
  emitted.
153
153
  ]=]
154
154
  function Subscription:Complete()
155
- if self._state ~= stateTypes.PENDING then
155
+ if self._state ~= SubscriptionStateTypes.PENDING then
156
156
  return
157
157
  end
158
158
 
159
- self._state = stateTypes.COMPLETE
159
+ self._state = SubscriptionStateTypes.COMPLETE
160
160
  if self._completeCallback then
161
161
  self._completeCallback()
162
162
  end
@@ -169,26 +169,34 @@ end
169
169
  @return boolean
170
170
  ]=]
171
171
  function Subscription:IsPending()
172
- return self._state == stateTypes.PENDING
172
+ return self._state == SubscriptionStateTypes.PENDING
173
173
  end
174
174
 
175
175
  function Subscription:_assignCleanup(task)
176
- assert(not self._cleanupTask, "Already have _cleanupTask")
176
+ assert(self._cleanupTask == nil, "Already have _cleanupTask")
177
177
 
178
- if task then
179
- if self._state ~= stateTypes.PENDING then
178
+ if MaidTaskUtils.isValidTask(task) then
179
+ if self._state ~= SubscriptionStateTypes.PENDING then
180
180
  MaidTaskUtils.doTask(task)
181
181
  return
182
182
  end
183
183
 
184
184
  self._cleanupTask = task
185
+ elseif task ~= nil then
186
+ error("Bad cleanup task")
185
187
  end
186
188
  end
187
189
 
188
190
  function Subscription:_doCleanup()
189
- if self._cleanupTask then
190
- local task = self._cleanupTask
191
- self._cleanupTask = nil
191
+ local task = self._cleanupTask
192
+ if not task then
193
+ return
194
+ end
195
+
196
+ self._cleanupTask = nil
197
+
198
+ -- The validity can change
199
+ if MaidTaskUtils.isValidTask(task) then
192
200
  MaidTaskUtils.doTask(task)
193
201
  end
194
202
  end
@@ -202,8 +210,8 @@ end
202
210
  :::
203
211
  ]=]
204
212
  function Subscription:Destroy()
205
- if self._state == stateTypes.PENDING then
206
- self._state = stateTypes.CANCELLED
213
+ if self._state == SubscriptionStateTypes.PENDING then
214
+ self._state = SubscriptionStateTypes.CANCELLED
207
215
  end
208
216
 
209
217
  self:_doCleanup()