0

Quick question, because I feel like I must be missing something. I'm using rxjs here because it's what I've got in-front of me, this is a general reactiveX question, I believe.

Let's say I have a set of Observables like so:

network_request = some_thing // An observable that produces the result of a network call
event_stream = network_request.flatMapLatest(function(v) {
    return connectToThing(v) // This is another observable that needs v
}) // This uses the result of the network call to form a long-term event-based connection

So, this works ok. Problem, though. Sometimes the connection thing fails.

So, if I do event_stream.retry() it works great. When it fails, it redoes the network call and gets a new v to use to make a new connection.

Problem

What happens if I want two things chained off of my network_request? Maybe I want the UI to do something every time the network call completes, like show something about v in the UI?

I can do:

shared = network_request.share() // Other implementations call this refCount
event_stream = shared.flatMapLatest(...) // same as above
ui_stream = shared.flatMapLatest(...) // Other transformation on network response

If I didn't do share then it would have made two requests, which isn't what I want, but with share, when event_stream later has an error, it doesn't retry the network request because the refcount is still at 1 (due to ui_stream), so it immediately returns completed.

What I want

This is obviously a small example I've made up to explain my confusion. What I want is that every time the result of event_stream (that long term connection) has an error all of the following happens:

  1. the network request is made again
  2. the new response of that request is used to build a new connection and event_stream goes on with new events like nothing happened
  3. that same response is also emitted in ui_stream to lead to further processing

This doesn't feel like a complicated thing, so I must just be misunderstanding something fundamental when it comes to splitting / fanning out RX things.

Workarounds I think I could do but would like to avoid

I'm looking to export these observables, so I can't just build them again and then say "Hey, here's the new thing". I want event_stream and all the downstream processing to not know there's been a disconnection. Same for ui_stream. It just got a new value.

I could probably work something out using a Subject as a generation counter that I ping every time I want everything to restart, and put the network_request into a flatMap based on that, so that I can break the share... But that feels like a really hacky solution, so I feel there has to be a better way than that.

What have I fundamentally misunderstood?

psycotica0
  • 3,130
  • 2
  • 19
  • 16
  • It's important to understand, that your `shared` observable doesn't know anything about errors happening in `event_stream` subscriber. `retry` just resubscribes every time it encounters `onError`, so for `shared` it is one more subscriber. Maybe I'm wrong, but I don't think you can solve this issue without some kind of feedback loop (which means introducing some kind of state). Better option would be rethinking your solution to get rid of this issue. – ionoy May 12 '16 at 13:38

2 Answers2

0

As I've been thinking about this more I've come to the same realization as ionoy, which is that retry just disconnects and reconnects, and upstream doesn't know it was due to an error.

When I thought about what I wanted, I realized I really wanted something like a chain, and also a spectator, so I've got this for now:

network_request = some_thing
network_shadow = new Rx.Subject()

event_stream = network_request.do(network_shadow).flatMapLatest(...)
ui_stream = network_shadow.whatever

This has the property where an retry in event_stream or downstream will cause the whole thing to restart, whereas ui_stream is its own thing. Any errors over there don't do anything, since network_shadow isn't actually a subscriber to event_stream, but it does peel the values off so long as the main event chain is running.

I feel like this isn't ideal, but it is better than what I was concerned I would have to do, which is have a restartEverything.onNext() in an doOnError, which would have been gross.

I'm going to work with this for now, and we'll see where it bites me...

psycotica0
  • 3,130
  • 2
  • 19
  • 16
0

You need to make your cold observable hot by using Publish. Read http://www.introtorx.com/Content/v1.0.10621.0/14_HotAndColdObservables.html#HotAndCold for a great explanation.

Slugart
  • 4,535
  • 24
  • 32
  • Yeah, I know about hot vs cold. The issue I was having was that I wanted to make it hot, but still have an error on one of my subscribers cause the whole thing to dispose. I considered writing a implementation that was the opposite of `refcount` where any time any subscriber unsubscribes I unsubscribe from upstream, but that would probably have led to issues, so I did this "side-channel" stuff. – psycotica0 May 18 '16 at 15:53