Занимаясь реактивным программированием, я часто сталкиваюсь с ситуациями, когда два потока зависят друг от друга. Каков идиоматический способ решения этих случаев?
Минимальный пример: есть кнопки A и B, обе отображают значение. Нажатие на A должно увеличить значение A на B. Нажатие на B должно установить значение B на A.
Первое решение, которое я смог придумать (пример на F#, но приветствуются ответы на любом языке):
let solution1 buttonA buttonB =
let mutable lastA = 0
let mutable lastB = 1
let a = new Subject<_> ()
let b = new Subject<_> ()
(OnClick buttonA).Subscribe(fun _ -> lastA <- lastA + lastB; a.OnNext lastA)
(OnClick buttonB).Subscribe(fun _ -> lastB <- lastA; b.OnNext lastB)
a.Subscribe(SetText buttonA)
b.Subscribe(SetText buttonA)
a.OnNext 0
b.OnNext 1
Это решение использует изменяемое состояние и темы, оно не очень читабельно и не выглядит идиоматичным.
Второе решение, которое я пробовал, включает в себя создание метода, который связывает два зависимых потока вместе:
let dependency (aGivenB: IObservable<_> -> IObservable<_>) (bGivenA: IObservable<_> -> IObservable<_>) =
let bProxy = new ReplaySubject<_> ()
let a = aGivenB bProxy
let b = bGivenA a
b.Subscribe(bProxy.OnNext)
a, b
let solution2 buttonA buttonB =
let aGivenB b =
Observable.WithLatestFrom(OnClick buttonA, b, fun click bValue -> bValue)
.Scan(fun acc x -> acc + x)
.StartWith(0)
let bGivenA a =
Observable.Sample(a, OnClick buttonB)
.StartWith(1)
let a, b = dependency aGivenB bGivenA
a.Subscribe(SetText buttonA)
b.Subscribe(SetText buttonB)
Это кажется немного лучше, но поскольку в реактивной библиотеке нет такого метода, как dependency
, я считаю, что существует более идиоматическое решение. Также легко ввести бесконечную рекурсию, используя второй подход.
Каков рекомендуемый подход к проблемам, связанным с циклической зависимостью между потоками, как в приведенном выше примере, в реактивном программировании?