Skip to content

Commit 1cbd5b2

Browse files
committedJan 14, 2020
flatten: avoid early completion
The flatten operator currently calls onCompleted as soon as the parent signals that it has completed. However, there could be more data incoming from the returned observables; in fact, the flattened observable might even be infinite. Track how many subscriptions are still active, and only call onCompleted at the time of the last completion.
1 parent 98f6eaa commit 1cbd5b2

File tree

4 files changed

+46
-10
lines changed

4 files changed

+46
-10
lines changed
 

‎rx.lua

+10-5
Original file line numberDiff line numberDiff line change
@@ -963,24 +963,29 @@ end
963963
function Observable:flatten()
964964
return Observable.create(function(observer)
965965
local subscriptions = {}
966+
local remaining = 1
966967

967968
local function onError(message)
968969
return observer:onError(message)
969970
end
970971

972+
local function onCompleted()
973+
remaining = remaining - 1
974+
if remaining == 0 then
975+
return observer:onCompleted()
976+
end
977+
end
978+
971979
local function onNext(observable)
972980
local function innerOnNext(...)
973981
observer:onNext(...)
974982
end
975983

976-
local subscription = observable:subscribe(innerOnNext, onError, util.noop)
984+
remaining = remaining + 1
985+
local subscription = observable:subscribe(innerOnNext, onError, onCompleted)
977986
subscriptions[#subscriptions + 1] = subscription
978987
end
979988

980-
local function onCompleted()
981-
return observer:onCompleted()
982-
end
983-
984989
subscriptions[#subscriptions + 1] = self:subscribe(onNext, onError, onCompleted)
985990
return Subscription.create(function ()
986991
for i = 1, #subscriptions do

‎src/operators/flatten.lua

+10-5
Original file line numberDiff line numberDiff line change
@@ -7,24 +7,29 @@ local util = require 'util'
77
function Observable:flatten()
88
return Observable.create(function(observer)
99
local subscriptions = {}
10+
local remaining = 1
1011

1112
local function onError(message)
1213
return observer:onError(message)
1314
end
1415

16+
local function onCompleted()
17+
remaining = remaining - 1
18+
if remaining == 0 then
19+
return observer:onCompleted()
20+
end
21+
end
22+
1523
local function onNext(observable)
1624
local function innerOnNext(...)
1725
observer:onNext(...)
1826
end
1927

20-
local subscription = observable:subscribe(innerOnNext, onError, util.noop)
28+
remaining = remaining + 1
29+
local subscription = observable:subscribe(innerOnNext, onError, onCompleted)
2130
subscriptions[#subscriptions + 1] = subscription
2231
end
2332

24-
local function onCompleted()
25-
return observer:onCompleted()
26-
end
27-
2833
subscriptions[#subscriptions + 1] = self:subscribe(onNext, onError, onCompleted)
2934
return Subscription.create(function ()
3035
for i = 1, #subscriptions do

‎tests/flatMap.lua

+13
Original file line numberDiff line numberDiff line change
@@ -19,4 +19,17 @@ describe('flatMap', function()
1919

2020
expect(observable).to.produce(1, 2, 3, 2, 3, 3)
2121
end)
22+
23+
it('completes after all observables produced by its parent', function()
24+
s = Rx.CooperativeScheduler.create()
25+
local observable = Rx.Observable.fromRange(3):flatMap(function(i)
26+
return Rx.Observable.fromRange(i, 3):delay(i, s)
27+
end)
28+
29+
local onNext, onError, onCompleted, order = observableSpy(observable)
30+
repeat s:update(1)
31+
until s:isEmpty()
32+
expect(#onNext).to.equal(6)
33+
expect(#onCompleted).to.equal(1)
34+
end)
2235
end)

‎tests/flatten.lua

+13
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,19 @@ describe('flatten', function()
1313
expect(observable).to.produce(1, 2, 3, 2, 3, 3)
1414
end)
1515

16+
it('completes after all observables produced by its parent', function()
17+
s = Rx.CooperativeScheduler.create()
18+
local observable = Rx.Observable.fromRange(3):map(function(i)
19+
return Rx.Observable.fromRange(i, 3):delay(i, s)
20+
end):flatten()
21+
22+
local onNext, onError, onCompleted, order = observableSpy(observable)
23+
repeat s:update(1)
24+
until s:isEmpty()
25+
expect(#onNext).to.equal(6)
26+
expect(#onCompleted).to.equal(1)
27+
end)
28+
1629
it('should unsubscribe from all source observables', function()
1730
local unsubscribeA = spy()
1831
local observableA = Rx.Observable.create(function(observer)

0 commit comments

Comments
 (0)
Please sign in to comment.