I have an endpoint that returns the items of the current page. Also, if you want to get next page's items, you have to construct a URL with the next page's hash. This hash returns as property in the response of the previous request. As you understand, we need a recursive network call.
When a request finishes, a new request will begin until hash returns null through the response.
Then, I need to bind all collected data into an array.
How can I build something like that in RxSwift? I would appreciate if you describe that concept.
You need something like this: https://gist.github.com/danielt1263/ec1032375498eb95aa260239b289d263
/**
Calls `producer` with `seed` then emits result and also passes it to `pred`. Will continue to call `producer` with new values as long as `pred` returns values.
- parameter seed: The starting value needed for the first producer call.
- parameter pred: This closure determines what the next value pass into producer should be or returns nil if no more calls are necessary.
- parameter producer: The function that produces the Single result.
- returns: An observable that emits each producer's value.
*/
func emitWhile<T, U>(seed: U, pred: #escaping (T) -> U?, producer: #escaping (U) -> Single<T>) -> Observable<T>
The seed is the hash for the first page. The pred takes the result of a network request, extracts the hash for the next page and returns it (or returns nil if there is no next page.) The producer takes the hash and returns an observable that will make the request for the page when it is subscribed to.
You will likely want to collect all the elements using either scan or reduce to emit all of the pages as a single array.
Related
I have defined a function for a request in Gatling -
def locations: HttpRequestBuilder = {
http("locations")
.get("/api/locations")
}
Now I need to call another request similar to the above one, only a query parameter difference. Like -
.get("/api/locations?key=value")
How can I define a function for the second one without repeating the entire block as for the first one and just adding the query?
Given a Completable called "completable". I want to convert completable to a Single providing a default value.
I originally used this approach:
completable.andThen(Single.just(defaultValue))
A colleague created a bugfix pull request replacing the line with:
completable.toSingleDefault(defaultValue)
While I have to admit this is easier to read anyway, I'm wondering: is this a bugfix or a refactoring?
the difference is in the error case.
completable.Single.just(dv): Returns a {#code Single} that emits a specified item.
Always emits Single.just(dv)
completable.toSingleDefault(dv): Converts this Completable into a Single which when this Completable completes normally, emits the given value through onSuccess.
Emits Single.error() or Single.just(dv) depending on the state of completable
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.