I have been using RxDart along my Flutter projects for a while. However I find it confusing that defining a BehaviorSubject you can listen directly to the subject, and also to the subject's stream. I wasn't really able to point out the difference.
Example:
BehaviorSubject<String> _mySubject = BehaviorSubject();
_mySubject.listen((value) => { print('Logging $value'} );
_mySubject.stream.listen((value) => { print('Logging $value'} );
Furthermore, BehaviorSubjects seem to offer two ways to access the last emitted value, from the docs I see the value getter which is synchronous. As well as the last getter returning a Future.
Once again I am confused. If nothing was emitted, then why isn't the value getter returning null? Instead, it just waits. The only workaround I found was to seed the subject with a null value.
Finally, I tried using the last getter as a Future and it never returns. Wether there is data or not. Calling it on the Subject just never seems to work.
EDIT:
Thanks #pskink for pointing out that the stream getter returns the Subject itself, therefore no difference between my two first examples.
value should return null if nothing has been emitted yet, not sure why it didn't work for you. Which version of RxDart are you using?
last on the other hand works a bit different. It will return the last value of a Stream after the Stream has been closed. So you need to call _mySubject.close(); then the Future will be completed (as long as the Stream has emitted any value, otherwise there will be an error).
Related
there is one thing I can't quite wrap my head around concerning null-safety in Dart, and that concerns how to safely retrieve values from Map<String,dynamic> (I've read the FAQ from the Dart docs).
Basically, the following code in the DartPad with null-safty enabled is valid:
void main() {
int i;
Map<String, dynamic> map = {"key": 1};
i = map["key"];
print(i);
}
Which I do not understand. Why can I assign map["key"] to i without the compiler shouting at me? From the docs:
Code should be safe by default. If you write new Dart code and don’t
use any explicitly unsafe features, it never throws a null reference
error at runtime.
But exactly this is happening. If, in the code above, the key is not in the map, or contains some random type, the program will crash on runtime, which I though is what should never happen with null safety.
I'm particular interested in this since I'm writing a Flutter app and don't understand how to properly deserialize the JSON data I fetch from the DB (try..catch? Special syntax like ??= ?). Even though I don't have the 'non-nullable' language feature enabled (I can't even write int? val without getting a warning), the compiler does not seem to mind that I assign nullable values to non-nullable variables, and will happily crash on runtime if they are null.
Basically, my question does not only concern null-safety, but the type system in general, since from my understanding it shouldn't be possible to assign a dynamic value to an int variable, but obviously this works with Map. Any explanation is greatly appreciated!
A dynamic variable can contain any data type, "dynamic" keyword is used when you don't know the specific data type that might be returned.
and what you're actually doing here:
i = map["key"];
is assigning a "dynamic" variable to an "int" variable and since dynamic in this case the value in the key/value pair of your Map is an integer which matches the data type "int" of variable "i" it won't crash because of type inference done at runtime and not compile time. if the "dynamic" variable was a String it would crash at runtime because of a type mismatch. Hope this is explanatory.
In the RxJava2 version of RxAndroidBle, the functions readCharacteristic() and writeCharacteristic() return Single<byte[]>.
The example code to read a characteristic is:
device.establishConnection(false).flatMap(rxBleConnection -> rxBleConnection.readCharacteristic(characteristicUUID))
But the documentation for flatMap() says the mapping function is supposed to return an ObservableSource. Here, it returns a Single. How can this work?
Update: I looked at possibilities using operators like .single() and .singleOrError() but they all seem to require that the upstream emits one item and then completes. But establishConnection() doesn't ever complete. (This is one reason I suggested that perhaps establishConnection() should be reimagined as a Maybe, and some other way be provided to disconnect rather than just unsubscribing.)
You're totally correct, this example cannot be compiled. it's probably leftover from RxJava1 version, where Single wasn't exists.
Simple fix with the same result is to use RxJava2 flatMapSingle for instance:
device.establishConnection(false)
.flatMapSingle(rxBleConnection -> rxBleConnection.readCharacteristic(characteristicUUID))
flatMapSingle accepts a Single as the return value, and will map the success value of the input Single to an emission from the upstream Observable.
The point is, that RxJava has more specific Observable types, that exposes the possible series of emission expected from this Observable. Some methods now return Single as this is the logical operation of their stream (readCharacteristic()), some Observable as they will emit more than single emission (establishConnection() - connection status that can be changed over time).
But RxJava2 also provided many operators to convert between the different types and it really depends on your needs and scenario.
Thanks Rob!
In fact, the README was deprecated and required some pimping here and there. Please have a look if it's ok now.
I think I found the answer I was looking for. The crucial point:
Single.fromObservable(observableSource) doesn't do anything until it receives the second item from observableSource! Assuming that the first item it receives is a valid emission, then if the second item is:
onComplete(), it passes the first item to onSuccess();
onNext(), it signals IndexOutOfBoundsException since a Single can't emit more than one item;
onError(), it presumably forwards the error downstream.
Now, device.establishConnection() is a 1-item, non-completing Observable. The RxBleConnecton it emits is flatMapped to a Single with readCharacteristic(). But (another gotcha), flatMapSingle subscribes to these Singles and combines them into an Observable, which doesn't complete until the source establishConnection() does. But the source doesn't ever complete! Therefore the Single we're trying to create won't emit anything, since it doesn't receive that necessary second item.
The solution is to force the generation of onComplete() after the first (and only) item, which can be done with take(1). This will satisfy the Single we're creating, and cause it to emit the Characteristic value we're interested in. Hope that's clear.
The code:
Single<byte[]> readCharacteristicSingle( RxBleDevice device, UUID characteristicUUID ) {
return Single.fromObservable(
device.establishConnection( false )
.flatMapSingle( connection -> connection.readCharacteristic( characteristicUUID ) )
.take( 1L ) // make flatMapSingle's output Observable complete after the first emission
// (this makes the Single call onSuccess())
);
}
I have a long-running operation that returns a value in code I don't control. I need that value to be published to things that ask for it. For this purpose I am using a BehaviorSubject:
var subject: Subject<Value>? = null
fun retrieveValue(): Single<Value> {
if (subject == null) {
subject = BehaviorSubject.create<Value>()
someOtherThing.retrieveValueAsync { value ->
subject.onNext(value)
}
}
return subject.singleOrError()
}
This lets me perform the operation only once and send the result as a single to all future interested parties. However, it does not work. The single will not emit a value until I call:
subject.onComplete()
But this is a problem because once the subject is completed future things can no longer subscribe to it.
What is the appropriate way to cache a value from another observable and pass it to a Single? If there was a way to have a subject automatically complete once its source observable emitted a value that would work. Single.cache() also looks promising, but I'm unsure how I would handle the fact that my value comes in asynchronously in that case.
It feels like I'm missing something silly.
There is a SingleSubject for this case.
If you don't want experimental code in your codebase, you can use ReplaySubject.createWithSize(1) and call onComplete without losing the last value, then convert it to Single.
I have been using the mapWithState API in Spark Streaming, but 2 things are not clear about the StateSpec.function:
Let's say my function is:
def trackStateForKey(batchTime: Time,
key: Long,
newValue: Option[JobData],
currentState: State[JobData]): Option[(Long, JobData)]
Why is the new value an Option[T] type? As far as I've seen, it was always defined for me, and since the method is supposed to be called with a new state, I don't really see the point why it could be optional.
What does the return value mean? I tried to find some pointers in the documentations and source code, but none of them describe what it is used for. Since I'm modifying the state of a key using state.remove() and state.update(), why would I have to do the same with return values?
In my current implementation I return None if I remove the key, and Some(newState) if I update it, but I'm not sure if that is correct.
Why is the new value an Option[T] type? As far as I've seen, it was
always defined for me, and since the method is supposed to be called
with a new state, I don't really see the point why it could be
optional.
It is an Option[T] for the reason that if you set a timeout using StateSpec.timeout, e.g:
StateSpec.function(spec _).timeout(Milliseconds(5000))
then the value passed in once the function times out will be None and the isTimingOut method on State[T] will yield true. This makes sense, because a timeout of the state doesn't mean that a new value has arrived for the specified key, and generally safer to use than passing null for T (which wouldn't work for primitives anyway) as you expect the user to safely operate on an Option[T].
You can see that in the Sparks implementation:
// Get the timed out state records, call the mapping function on each and collect the
// data returned
if (removeTimedoutData && timeoutThresholdTime.isDefined) {
newStateMap.getByTime(timeoutThresholdTime.get).foreach { case (key, state, _) =>
wrappedState.wrapTimingOutState(state)
val returned = mappingFunction(batchTime, key, None, wrappedState) // <-- This.
mappedData ++= returned
newStateMap.remove(key)
}
}
What does the return value mean? I tried to find some pointers in the
documentations and source code, but none of them describe what it is
used for. Since I'm modifying the state of a key using state.remove()
and state.update(), why would I have to do the same with return
values?
The return value is a way to pass intermediate state along the spark graph. For example, assume that I want to update my state but also perform some operation in my pipeline with the intermediate data, e.g:
dStream
.mapWithState(stateSpec)
.map(optionIntermediateResult.map(_ * 2))
.foreachRDD( /* other stuff */)
That return value is exactly what allows me to continue operating on said data. If you don't care for the intermediate result and only want the complete state, then outputting None is perfectly fine.
Edit:
I've written a blog post (following this question) which attempts to give an in-depth explanation to the API.
i.e., by passing the error condition and not halting the entire Observable?
My Observable starts with a user-supplied list of package tracking numbers from common delivery services (FedEx, UPS, DHL, etc), looks up the expected delivery date online, then returns those dates in terms of number of days from today (i.e. "in 3 days" rather than "Jan 22"). The problem is that if any individual lookup results in an exception, the entire stream halts, and the rest of the codes won't be looked up. There's no ability to gracefully handle, say, UnknownTrackingCode Exception, and so the Observable can't guarantee that it will look up all the codes the user submitted.
public void getDaysTillDelivery(List<String> tracking_code_list) {
Observable o = Observable.from(tracking_code_list)
// LookupDeliveryDate performs network calls to UPS, FedEx, USPS web sites or APIs
// it might throw: UnknownTrackingCode Exception, NoResponse Exception, LostPackage Exception
.map(tracking_code -> LookupDeliveryDate(tracking_code))
.map(delivery_date -> CalculateDaysFromToday(delivery_date));
o.subscribe(mySubscriber); // will handle onNext, onError, onComplete
}
Halting the Observable stream as a result of one error is by design:
http://reactivex.io/documentation/operators/catch.html
Handling Exceptions in Reactive Extensions without stopping sequence
https://groups.google.com/forum/#!topic/rxjava/trm2n6S4FSc
The default behavior can be overcome, but only by eliminating many of the benefits of Rx in the first place:
I can wrap LookupDeliveryDate so it returns Dates in place of Exceptions (such as 1899-12-31 for UnknownTrackingCode Exception) but this prevents "loosely coupled code", because CalculateDaysFromToday would need to handle these special cases
I can surround each anonymous function with try/catch and blocks, but this essentially prevents me from using lambdas
I can use if/thens to direct the code path, but this will likely require maintaining some state and eliminating deterministic evaluation
Error handling of each step, obviously, prevents consolidating all error handling in the Subscriber
Writing my own error-handling operator is possible, but thinly documented
Is there a better way to handle this?
What exactly do you want to happen if there is an error? Do you just want to throw that entry away or do you want something downstream to do something with it?
If you want something downstream to take some action, then you are really turning the error into data (sort of like your example of returning a sentinel value of 1899-12-31 to represent the error). This strategy by definition means that everything downstream needs to understand that the data stream may contain errors instead of data and they must be modified to deal with it.
But rather than yielding a magic value, you can turn your Observable stream into a stream of Either values. Either the value is a date, or it is an error. Everything downstream receives this Either object and can ask it if it has a value or an error. If it has a value, they can produce a new Either object with the result of their calculation. If it has an error and they cannot do anything with it, they can yield an error Either themselves.
I don't know Java syntax, but this is what it might look like in c#:
Observable.From(tracking_code_list)
.Select(t =>
{
try { return Either.From(LookupDeliveryDate(t)); }
catch (Exception e)
{
return Either.FromError<Date>(e);
}
})
.Select(dateEither =>
{
return dateEither.HasValue ?
Either.From(CalculateDaysFromToday(dateEither.Value)) :
Either.FromError<int>(dateEither.Error);
})
.Subscribe(value =>
{
if (value.HasValue) mySubscriber.OnValue(value.Value);
else mySubscribe.OnError(value.Error);
});
Your other option is the "handle"/suppress the error when it occurs. This may be sufficient depending on your needs. In this case, just have LookupDeliveryDate return magic dates instead of exceptions and then add a .filter to filter out the magic dates before they get to CalculateDaysFromToay.