-
Notifications
You must be signed in to change notification settings - Fork 67
Description
I'm using the RxFeedback in my project, and i got a problem that a processing network request will be cancelled by the follwing request.
To describe the question clearly, i made a demo here.
the UI is below, it's simple
If 'request 1' button taped, a simulative request will be send, and delay to 2.0s, the button below will show 'response 1 received'.
and if 'request 2' button taped, 'response 2 received' will display the same way.
While, the problem is that if 'request 1' button taped, and before response 1 received, i taped the 'request 2' button, then the response 1 will never come back, it is cancelled.
And if i make debug here:
let event = just.delay(2.0) .debug(query.debugIdentifier, trimOutput: false)
i will got the following log:
---- request 1 ----
-> subscribed
---- request 2 ----
-> subscribed
---- request 1 ----
-> isDisposed
---- request 2 ----
-> Event next(response(RxFeedbackQueryTest.RequestType.second))
---- request 2 ----
-> Event completed
---- request 2 ----
-> isDisposed
we can see that request 1 was disposed once request 2 is subscribed
after reading the source codes of RxFeedback, i think, the codes leading the problem is below:
by using 'flatMapLatest' and 'switchLatest' operators, the previous sequence will be disposed,
why did you design to cancel the previous 'query' if a new 'query' comes?
can i just replace the 'flatMapLatest' with 'flatMap', and remove the operation 'takeUntilWithCompletedAsync'?
and how 'takeUntilWithCompletedAsync' can avoid the reentrancy issues?
The main code is below:
` private func setupBinding() {
let UIBindings: (Driver<State>) -> Signal<Event> = bind(self) { me, state in
let subscriptions = [
state.map { $0.response1 }.drive(me.responseLabel1.rx.text),
state.map { $0.response2 }.drive(me.responseLabel2.rx.text)
]
let events: [Signal<Event>] = [
me.requestButton1.rx.tap.map { Event.request(.first) }.asSignal(onErrorJustReturn: .none),
me.requestButton2.rx.tap.map { Event.request(.second) }.asSignal(onErrorJustReturn: .none),
me.clearButton.rx.tap.map { Event.clear }.asSignal(onErrorJustReturn: .none)
]
return Bindings(subscriptions: subscriptions, events: events)
}
let nonUIBindings: (Driver<State>) -> Signal<Event> = react(query: { (state) -> Set<RequestType> in
let querys = Set(state.requestsToSend.all())
return querys
}) { (query) -> Signal<Event> in
let just = Signal.just(Event.response(query))
let event = just.delay(2.0)
.debug(query.debugIdentifier, trimOutput: false)
return event
}
Driver
.system(initialState: State(),
reduce: State.reduce,
feedback: UIBindings, nonUIBindings)
.drive()
.disposed(by: bag)
}`
`enum Event {
case none
case request(RequestType)
case response(RequestType)
case clear
}
struct State: Mutable {
var requestsToSend = Requests()
var response1: String?
var response2: String?
static func reduce(state: State, event: Event) -> State {
switch event {
case .request(let req):
return state.mutateOne {
$0.requestsToSend.append(req)
}
case .response(let req):
return state.mutateOne {
switch req {
case .first:
$0.response1 = req.responseString
case .second:
$0.response2 = req.responseString
}
}
case .clear:
return state.mutateOne {
$0.response1 = nil
$0.response2 = nil
_ = $0.requestsToSend.all()
}
default: return state
}
}
}`