I was wondering if there is a convenient method to check if an observable has been completed. For instance I have a test
test("An observable that tracks another observable is completed")
{
val sub = PublishSubject[Boolean](false)
val newOb = sub recovered // This methods returns an Observable[Try[T]]
val res = scala.collection.mutable.ListBuffer[Try[Boolean]]()
val cr = newOb subscribe( v => res += v, t => assert( false, "There shouldn't be an exception" ), () => println("Stream Completed") )
sub.onNext(true)
sub.onNext(false)
sub.onNext(true)
sub.onCompleted
assert( res.toList === List(Success(true), Success(false), Success(true) ))
newOb.isEmpty subscribe { v => assert( v == true, "Stream should be completed" ) }
}
The recovered method returns an Observable[Try[T]] and is an extension to the standard Observable. I want to check that the Observable[Try[T]] is completed when the source Observable is completed.
So I wrote a test with a Subject to which I Publish a few values and then eventually complete. Is there a simple way I can check to see that newOb is also completed? There is no method like isCompleted in Observable.
This is the essence of the pattern Observer, when there is a call onCompleted, the appropriate handler is triggered, and only it can be understood that the Observer completed. But I have heard that if the Observer has been completed and it is attached to the handler, it works immediately, but I think it has already been implemented at a lower level where asJavaObserver.
That link may help:
Netflix RxJava
Related
I have an hot IObservable<T> which may throw an exception. However, I would like to continue with it. I think I could use Retry operator for that. However, it would be great if I can also listen to any error in IObservable<T> through a separate IObservable<Exception>. Is it possible?
Your case is significantly more simplified in that you have a hot observable.
OnError is a notification outside your value stream, so we could materialize the notifications to retrieve the error. This still causes the tear-down of the stream with an OnCompleted, so you'll need to re-subscribe with Repeat.
var exceptions =
source
.Materialize()
.Where(notif => notif.Kind == NotificationKind.OnError)
.Select(notif => notif.Exception)
.Repeat();
Note
If you're using a Subject<T> for your hot observable, you might run into the usual problem of re-subbing a subject. A subject will replay its OnError or OnCompleted notifications for every new observer.
var source = new Subject<int>();
source.OnNext(1);
source.OnError(new Exception());
source.Subscribe(
i => Console.WriteLine(i),
ex => Console.WriteLine("Still got exception after the throw")
);
In this case your exception stream will go into an infinite re-subscription loop.
The premise of your question violates the observable contract:
An Observable may make zero or more OnNext notifications, each representing a single emitted item, and it may then follow those emission notifications by either an OnCompleted or an OnError notification, but not both. Upon issuing an OnCompleted or OnError notification, it may not thereafter issue any further notifications. (emphasis mine)
In other words, after your hot IObservable<T> throws an exception, the observable is ended. The observable of exceptions that comes out of that has a max count of one.
If you want to support a scenario where you re-start an observable after an exception, you're producing a stream of observables, or IObservable<IObservable<T>>. To work with that, here's a code sample:
var source = new Subject<Subject<int>>();
var exceptionStream = source
.SelectMany(o => o.Materialize())
.Where(n => n.Kind == NotificationKind.OnError)
.Select(n => n.Exception);
var itemStream = source
.SelectMany(o => o.Materialize())
.Where(n => n.Kind == NotificationKind.OnNext)
.Select(n => n.Value);
var items = new List<int>();
var exceptions = new List<Exception>();
itemStream.Subscribe(i => items.Add(i));
exceptionStream.Subscribe(e => exceptions.Add(e));
var currentSubject = new Subject<int>();
source.OnNext(currentSubject);
currentSubject.OnNext(1);
currentSubject.OnNext(2);
currentSubject.OnNext(3);
currentSubject.OnError(new Exception("First error"));
var currentSubject2 = new Subject<int>();
source.OnNext(currentSubject2);
currentSubject2.OnNext(4);
currentSubject2.OnNext(5);
currentSubject2.OnNext(6);
currentSubject2.OnError(new Exception("Second error"));
items.Dump(); //Linqpad
exceptions.Dump(); //Linqpad
I've just started learning about #ngrx/store and #ngrx.effects and have created my first effect in my Angular/Ionic app. It runs ok the first time but if I dispatch the event to the store again (i.e when clicking the button again), nothing happens (no network call is made, nothing in console logs). Is there something obvious I'm doing wrong? Here's the effect:
#Effect() event_response$ = this.action$
.ofType(SEND_EVENT_RESPONSE_ACTION)
.map(toPayload)
.switchMap((payload) => this.myService.eventResponse(payload.eventId,payload.response))
.map(data => new SentEventResponseAction(data))
.catch((error) => Observable.of(new ErrorOccurredAction(error)));
Thanks
It sounds like an error is occurring. In that situation, the action in the observable returned by catch will be emitted into the effect's stream and the effect will then complete - which will prevent the effect from running after the error action is emitted.
Move the map and the catch into the switchMap:
#Effect() event_response$ = this.action$
.ofType(SEND_EVENT_RESPONSE_ACTION)
.map(toPayload)
.switchMap((payload) => this.myService
.eventResponse(payload.eventId, payload.response)
.map(data => new SentEventResponseAction(data))
.catch((error) => Observable.of(new ErrorOccurredAction(error)))
);
Composing the catch within the switchMap will prevent the effect from completing if an error occurs.
You must move map() and catchError() into swithchMap() as following
#Effect()
public event_response$ = this.action$.pipe(
ofType(SEND_EVENT_RESPONSE_ACTION),
switchMap((payload) => {
return this.myService.eventResponse(payload.eventId,payload.response).pipe(
map((data: DataType) => new SentEventResponseAction(data)),
catchError((error) => Observable.of(new ErrorOccurredAction(error)))
})
);
);
Please note that, evetResponse() method inside myService should return an observable in order to use pipe afterward.
In case your method inside service returns Promise, you can convert it into an observable by the use of from in the rxjs package as below:
import { from } from 'rxjs';
...
const promise = this.myService.eventResponse(payload.eventId,payload.response);
const observable = from(promise);
return observable.pipe(...
For more and detail description take a look at this link
I have a problem with my Rx subscription using Switch statement.
_performSearchSubject
.AsObservable()
.Select(_ => PerformQuery())
.Switch()
.ObserveOn(_synchronizationContextService.SynchronizationContext)
.Subscribe(DataArrivedForPositions, PositionQueryError, PositionQueryCompleted)
.DisposeWith(this);
The flow is:
Some properties change and the performSearchSubject.OnNext is called
The PerformPositionQuery() is called, which returns a observer each time it is hit
The service which responds through this observer calls OnNext twice and OnCompleted once when the data receive is done
Method DataArrivedForPositions is called twice as expected
Method PositionQueryCompleted is never called, though observer.OnCompleted() is called inside my data service.
Code for dataService is:
protected override void Request(Request request, IObserver<Response> observer)
{
query.Arrive += p => QueryReceive(request.RequestId, p, observer, query);
query.Error += (type, s, message) => QueryError(observer, message);
query.NoMoreData += id => QueryCompleted(observer);
query.Execute(request);
}
private void QueryError(IObserver<PositionSheetResponse> observer, string message)
{
observer.OnError(new Exception(message));
}
private void QueryCompleted(IObserver<PositionSheetResponse> observer)
{
observer.OnCompleted();
}
private void QueryReceive(Guid requestId, Qry0079Receive receiveData, IObserver<PositionSheetResponse> observer, IQry0079PositionSheet query)
{
observer.OnNext(ConvertToResponse(requestId, receiveData));
}
Switch result will only Complete when your outer observable (_performSearchSubject) completes. I assume in your case this one never does (it's probably bound to a user action performing the search).
What's unclear is when you expect PositionQueryCompleted to be called. If It's after each and every successful query is processed, then your stream needs to be modified, because Switch lost you the information that the query stream completed, but it also lacks information about the UI (wrong scheduler even) to say whether its data was actually processed.
There may be other ways to achieve it, but basically you want your query stream complete to survive through Switch (which currently ignore this event). For instance you can transform your query stream to have n+1 events, with one extra for the complete:
_performSearchSubject
.AsObservable()
.Select(_ =>
PerformQuery()
.Select(Data => new { Data, Complete = false})
.Concat(Observable.Return(new { Data = (string)null, Complete = true })))
You can safely apply .Switch().ObserveOn(_synchronizationContextService.SynchronizationContext) on it, but then you need to modify your subscription:
.Subscribe(data => {
if (data.Complete) DataArrivedForPositions(data.Data);
else PositionQueryCompleted()
}, PositionQueryError)
Here is the sample code flow:
class FSMActor{
when(Idle) {
case Event(Start, Uninitialized) =>
case Event(InitMap(inMap), t # EvaluteRuleMap(v, c)) =>
logger.info(s"State = $stateName, Event = Event(_, InitMap(inMap))")
goto(EVALRule) using t.copy(ruleMap = inMap)
}
when(EVALRule) {
case Event(RowMap(m), t # EvaluteRuleMap(v, c)) =>
logger.debug("input row map m " + m)
**if ( <somecondition> ) { // If comment this if-else block, I could see rowMaps being received.
logger.debug(s"Moving to State Trigger x=$x")
goto(TriggerRule) using t.copy(ruleMap = x.get)
} else {
logger.debug(s"staying in EVALRule, x = $x")
stay
}**
}
when(TriggerRule) {
case Event(_, _) => ....
}
}
}
When the control in in "EVALRule" state, It will keep receiving streams maps(RowMap) and based on some computation, it moves to trigger rule.
Unfortunately for some weird reason, some of the incoming RowMaps are not being received at "case Event(RowMap(m), t # EvaluteRuleMap(v, c)) =>" and
If I comment the code bock (bolded in the above code) then I could see all incoming rowmaps being received.
Could anyone let me know why is so? I've been trying to find the cause but couldn't get it to.
Appreciate your help, thanks.
When if ( <somecondition> ) is true, you are moving to the TriggerRule state. In that state you are looking for messages of type EVENT instead of Event (all caps). So the message is not handled by the FSM.
In general, when missing messages in FSM, the best way to debug is to write a whenUnhandled block with a log/print statement to see what messages are not handled by the states you have defined.
There was some issue with message handling in the code itself, we debugged it and fixed the issue, now its working seamlessly.
Let's say you have the following Observable in rxjava-scala-0.18.4
#volatile var dorun = true
var subscriber: Subscriber[String] = null
val myObs = Observable { obs: Subscriber[String] =>
subscriber = obs
Subscription { println("unsubscribed"); dorun = false }
}
val sub = myObs.head.subscribe(println(_))
assertTrue(dorun)
subscriber.onNext("hello")
Thread.sleep(500)
assertFalse(dorun)
subscriber.onNext("world")
Thread.sleep(500)
assertFalse(dorun)
The second assertion fails, which means that head does not unsubscriby. Is my understanding of Observables wrong or should head unsubscribe after the first element got emitted?
Take a look at your subscribe() method: you loop until run is set to false, but the only way for that to happen would be to close the subscription. The problem is that nobody has the subscription yet: the loop keeps you from returning. The head operator can't terminate the underlying subscription after the first item is delivered because it hasn't finished subscribing yet. Thus, you just keep looping forever.
One solution would be to move your loop into an action scheduled on Schedulers.trampoline(). Then the events would be delivered some time after returning from subscribe().
Additionally, in your subscribe() method, it seems you need to add the new subscription object to the Subscriber that gets passed in, like so:
val myObs = Observable {
obs: rx.lang.scala.Subscriber[String] =>
...
obs.add(
Subscription {
dorun = false
println("unsubscribed")
}
)
}