You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
315 lines
13 KiB
315 lines
13 KiB
--===========================================================================--
|
|
-- --
|
|
-- System.Reactive.Subject --
|
|
-- --
|
|
--===========================================================================--
|
|
|
|
--===========================================================================--
|
|
-- Author : kurapica125@outlook.com --
|
|
-- URL : http://github.com/kurapica/PLoop --
|
|
-- Create Date : 2019/12/01 --
|
|
-- Update Date : 2019/12/01 --
|
|
-- Version : 1.0.0 --
|
|
--===========================================================================--
|
|
|
|
PLoop(function(_ENV)
|
|
namespace "System.Reactive"
|
|
|
|
--- A bridge or proxy that acts both as an observer and as an Observable
|
|
__Sealed__() class "Subject" (function(_ENV)
|
|
extend "System.IObservable" "System.IObserver"
|
|
|
|
export { Observer, Dictionary, next = next, type = type, pairs = pairs }
|
|
|
|
FIELD_NEW_SUBSCRIBE = "__Subject_New"
|
|
|
|
-----------------------------------------------------------------------
|
|
-- property --
|
|
-----------------------------------------------------------------------
|
|
--- The obervable that provides the items
|
|
property "Observable" { type = IObservable }
|
|
|
|
--- The observer that will consume the items
|
|
property "Observers" { set = false, default = function() return Dictionary() end }
|
|
|
|
-----------------------------------------------------------------------
|
|
-- method --
|
|
-----------------------------------------------------------------------
|
|
local function subscribe(self, observer)
|
|
local obs = self.Observers
|
|
if obs[observer] then return observer end
|
|
local hasobs = next(obs)
|
|
|
|
-- Bind the Unsubscribe event
|
|
local onUnsubscribe
|
|
onUnsubscribe = function()
|
|
observer.OnUnsubscribe = observer.OnUnsubscribe - onUnsubscribe
|
|
|
|
obs[observer] = nil
|
|
if self.Observable and not next(obs) then self:Unsubscribe() end
|
|
end
|
|
observer.OnUnsubscribe = observer.OnUnsubscribe + onUnsubscribe
|
|
|
|
-- Subscribe the subject
|
|
self:Resubscribe()
|
|
if self[FIELD_NEW_SUBSCRIBE] then
|
|
if self[FIELD_NEW_SUBSCRIBE] == true then
|
|
self[FIELD_NEW_SUBSCRIBE] = {}
|
|
end
|
|
|
|
self[FIELD_NEW_SUBSCRIBE][observer] = true
|
|
else
|
|
obs[observer] = true
|
|
end
|
|
if self.Observable and not hasobs then self.Observable:Subscribe(self) end
|
|
|
|
return observer
|
|
end
|
|
|
|
--- Notifies the provider that an observer is to receive notifications.
|
|
__Arguments__{ IObserver }
|
|
Subscribe = subscribe
|
|
|
|
__Arguments__{ Callable/nil, Callable/nil, Callable/nil }
|
|
function Subscribe(self, onNext, onError, onCompleted)
|
|
return subscribe(self, Observer(onNext, onError, onCompleted))
|
|
end
|
|
|
|
function OnNextCore(self, ...) return self:OnNext(...) end
|
|
function OnErrorCore(self, e) return self:OnError(e) end
|
|
function OnCompletedCore(self) return self:OnCompleted() end
|
|
|
|
--- Provides the observer with new data
|
|
function OnNext(self, ...)
|
|
if not self.IsUnsubscribed then
|
|
self[FIELD_NEW_SUBSCRIBE] = self[FIELD_NEW_SUBSCRIBE] or true
|
|
|
|
local onNext = self.OnNextCore
|
|
for k in self.Observers:GetIterator() do
|
|
onNext(k, ...)
|
|
end
|
|
|
|
local newSubs = self[FIELD_NEW_SUBSCRIBE]
|
|
if newSubs and type(newSubs) == "table" then
|
|
for observer in pairs(newSubs) do
|
|
self.Observers[observer] = true
|
|
end
|
|
end
|
|
self[FIELD_NEW_SUBSCRIBE] = false
|
|
end
|
|
end
|
|
|
|
--- Notifies the observer that the provider has experienced an error condition
|
|
function OnError(self, exception)
|
|
if self.IsUnsubscribed then return end
|
|
|
|
local onError = self.OnErrorCore
|
|
for k in self.Observers:GetIterator() do
|
|
onError(k, exception)
|
|
end
|
|
end
|
|
|
|
--- Notifies the observer that the provider has finished sending push-based notifications
|
|
function OnCompleted(self)
|
|
if self.IsUnsubscribed then return end
|
|
|
|
local onComp = self.OnCompletedCore
|
|
for k in self.Observers:GetIterator() do
|
|
onComp(k)
|
|
end
|
|
end
|
|
|
|
-----------------------------------------------------------------------
|
|
-- constructor --
|
|
-----------------------------------------------------------------------
|
|
__Arguments__{ IObservable, Callable/nil, Callable/nil, Callable/nil }
|
|
function __ctor(self, observable, onNext, onError, onCompleted)
|
|
self[FIELD_NEW_SUBSCRIBE] = false
|
|
|
|
self.Observable = observable
|
|
self.OnNextCore = onNext
|
|
self.OnErrorCore = onError
|
|
self.OnCompletedCore= onCompleted
|
|
end
|
|
|
|
__Arguments__{ Callable/nil, Callable/nil, Callable/nil }
|
|
function __ctor(self, onNext, onError, onCompleted)
|
|
self[FIELD_NEW_SUBSCRIBE] = false
|
|
|
|
self.OnNextCore = onNext
|
|
self.OnErrorCore = onError
|
|
self.OnCompletedCore= onCompleted
|
|
end
|
|
end)
|
|
|
|
--- Only emits the last value (and only the last value) emitted by the source Observable,
|
|
-- and only after that source Observable completes
|
|
__Sealed__() class "AsyncSubject" (function(_ENV)
|
|
inherit "Subject"
|
|
|
|
export { select = select, unpack = _G.unpack or table.unpack }
|
|
|
|
-----------------------------------------------------------------------
|
|
-- method --
|
|
-----------------------------------------------------------------------
|
|
--- Provides the observer with new data
|
|
function OnNext(self, ...)
|
|
if not self.IsUnsubscribed then
|
|
self[0] = select("#", ...)
|
|
for i = 1, self[0] do
|
|
self[i] = select(i, ...)
|
|
end
|
|
end
|
|
end
|
|
|
|
--- Notifies the observer that the provider has finished sending push-based notifications
|
|
function OnCompleted(self)
|
|
if self[0] > 0 then super.OnNext(self, unpack(self, 1, self[0])) end
|
|
super.OnCompleted(self)
|
|
end
|
|
end)
|
|
|
|
--- Emitting the item most recently emitted by the source Observable (or a seed/default value
|
|
-- if none has yet been emitted) and then continues to emit any other items emitted later by the source Observable
|
|
__Sealed__() class "BehaviorSubject" (function(_ENV)
|
|
inherit "Subject"
|
|
|
|
export { select = select, max = math.max, unpack = _G.unpack or table.unpack, onNext = Subject.OnNext }
|
|
|
|
-----------------------------------------------------------------------
|
|
-- method --
|
|
-----------------------------------------------------------------------
|
|
function Subscribe(self, ...)
|
|
local observer = super.Subscribe(self, ...)
|
|
if self[0] > 0 then observer:OnNext(unpack(self, 1, self[0])) end
|
|
end
|
|
|
|
--- Provides the observer with new data
|
|
function OnNext(self, ...)
|
|
self[0] = max(1, select("#", ...))
|
|
for i = 1, self[0] do
|
|
self[i] = select(i, ...)
|
|
end
|
|
|
|
onNext(self, ...)
|
|
end
|
|
|
|
-----------------------------------------------------------------------
|
|
-- constructor --
|
|
-----------------------------------------------------------------------
|
|
__Arguments__{ IObservable, Any * nil }
|
|
function __ctor(self, observable, ...)
|
|
self[0] = select("#", ...)
|
|
for i = 1, self[0] do
|
|
self[i] = select(i, ...)
|
|
end
|
|
|
|
super(self, observable)
|
|
end
|
|
|
|
__Arguments__{ Any * nil }
|
|
function __ctor(self, ...)
|
|
self[0] = select("#", ...)
|
|
for i = 1, self[0] do
|
|
self[i] = select(i, ...)
|
|
end
|
|
|
|
super(self)
|
|
end
|
|
end)
|
|
|
|
--- Emits to an observers only when connect to the observable source
|
|
__Sealed__() class "PublishSubject" (function(_ENV)
|
|
inherit "Subject" extend "IConnectableObservable"
|
|
|
|
export { Observable, Subject }
|
|
|
|
-----------------------------------------------------------------------
|
|
-- property --
|
|
-----------------------------------------------------------------------
|
|
property "PublishObservable" { type = IObservable }
|
|
|
|
-----------------------------------------------------------------------
|
|
-- method --
|
|
-----------------------------------------------------------------------
|
|
function Connect(self)
|
|
self.PublishObservable:Subscribe(self)
|
|
return self
|
|
end
|
|
|
|
function RefCount(self)
|
|
return Subject(self.PublishObservable)
|
|
end
|
|
|
|
-----------------------------------------------------------------------
|
|
-- constructor --
|
|
-----------------------------------------------------------------------
|
|
__Arguments__{ IObservable }
|
|
function __ctor(self, observable)
|
|
self.PublishObservable = observable
|
|
end
|
|
end)
|
|
|
|
--- Emits to any observer all of the items that were emitted by the source Observable(s), regardless of when the observer subscribes
|
|
__Sealed__() class "ReplaySubject" (function(_ENV)
|
|
inherit "Subject"
|
|
|
|
export { Queue, select = select }
|
|
|
|
-----------------------------------------------------------------------
|
|
-- property --
|
|
-----------------------------------------------------------------------
|
|
--- The replay item count
|
|
property "QueueCount" { type = Number, default = 0 }
|
|
|
|
--- The last values from the source observable
|
|
property "Queue" { set = false, default = function() return Queue() end }
|
|
|
|
--- The max length of the buff size
|
|
property "QueueSize" { type = Number, default = math.huge }
|
|
|
|
-----------------------------------------------------------------------
|
|
-- method --
|
|
-----------------------------------------------------------------------
|
|
function Subscribe(self, ...)
|
|
local observer = super.Subscribe(self, ...)
|
|
if self.Queue:Peek() then
|
|
local queue = self.Queue
|
|
local index = 1
|
|
|
|
local count = queue:Peek(index, 1)
|
|
while count do
|
|
observer:OnNext(queue:Peek(index + 1, count))
|
|
index = index + 1 + count
|
|
count = queue:Peek(index, 1)
|
|
end
|
|
end
|
|
end
|
|
|
|
--- Provides the observer with new data
|
|
function OnNext(self, ...)
|
|
self.Queue:Enqueue(select("#", ...), ...)
|
|
if self.QueueCount + 1 > self.QueueSize then
|
|
self.Queue:Dequeue(self.Queue:Dequeue())
|
|
else
|
|
self.QueueCount = self.QueueCount + 1
|
|
end
|
|
super.OnNext(self, ...)
|
|
end
|
|
|
|
-----------------------------------------------------------------------
|
|
-- constructor --
|
|
-----------------------------------------------------------------------
|
|
__Arguments__{ IObservable, Number/nil }
|
|
function __ctor(self, observable, max)
|
|
self.QueueSize = max
|
|
super(self, observable)
|
|
end
|
|
|
|
__Arguments__{ Number/nil }
|
|
function __ctor(self, max)
|
|
self.QueueSize = max
|
|
super(self)
|
|
end
|
|
end)
|
|
end)
|
|
|