ReactiveCommand.Execute not triggering IsExecuting - reactiveui

I'm subscribed to the IsExecuting of a command:
LoginCommand.IsExecuting.Subscribe(x => Log("Logging in"));
and it works fine when my Command is invoked by InvokeCommand but when I call it by:
LoginCommand.Execute();
The IsExecuting observable is not triggered.
This works:
Observable.Start(() => { }).InvokeCommand(LoginCommand);
Does someone know why the IsExecuting property doesn't change when calling the Execute method? I'm trying to unit test the command so I thought this would be the best way to execute it from tests.

After the upgrade to ReactiveUI 7.0, the Execute() method changed. Right now it does not trigger the command immediately. Instead, it returns a cold IObservable to which you have to subscribe in order to make stuff happen.
LoginCommand.Execute().Subscribe();
Check in the write up about the changes in RxUI 7.0 in the release notes. Ctrl+F "ReactiveCommand is Better". It states explicitly:
the Execute exposed by ReactiveCommand is reactive (it returns IObservable). It is therefore lazy and won't do anything unless something subscribes to it.

When you want to execute an ReactiveCommand, you can do it like this:
RxApp.MainThreadScheduler.Schedule(Unit.Default, (scheduler, state) =>
ViewModel.MyCommand.Execute(state).Subscribe());
You can then Subscribe to it like this:
this.WhenActivated(d => {
MyCommand
.Select(_ => this)
.ObserveOn(RxApp.MainThreadScheduler)
.ExecuteOn(RxApp.TaskScheduler)
.Subscribe(viewModel => {
// ...
})
.DisposeWith(d);
});

Related

Do the take() and takeUntil() RxJS cause a memory leak?

From the RxJS documentation I see the following example:
const source = interval(1000);
const clicks = fromEvent(document, 'click');
const result = source.pipe(takeUntil(clicks));
result.subscribe(x => console.log(x));
This is close to a code pattern needed for my app but I see a problem. The takeUntil operator subscribes, but as I understand it an Observer has no way to unsubscribe from the source Observable. It has no access to a Subscription object on which it can call unsubscribe().
So if I understand this correctly then once the user clicks the source observable will continue to emit ticks forever to the takeUntil which will consume them and do nothing with them.
Am I reading this correctly? If so is there a generally accepted way to kill the source observable from within the Observer pipe?
What happens with takeUntil is the following.
When the Observable passed to takeUntil as parameter notifies a value, the subscriber of the Observable returned by takeUntil completes and, as a consequence, all the subscriptions created in the pipe chain are unsubscribed one after the other in reverse order.
In simpler words, the unsubscription is performed behind the scene by the RxJs internal mechanisms.
To prove this behavior you can try this code
const source = interval(1000).pipe(
tap({ next: (val) => console.log('source value', val) })
);
const clicks = fromEvent(document, 'click');
const result = source.pipe(takeUntil(clicks));
result.subscribe((x) => console.log(x));
If you run it, you see that the message 'source value', val is printed until the click occurs. After this, no more message is printed on the console, which means that the Observable upstream, i.e. the Observable created by the interval function does not notify any more.
You can try the above code in this stackblitz.
SOME DETAILS ON THE INTERNALS
We can take a look at the internals of the RxJs implementation to see how this unsubscribe behind the scenes works.
Let's start from takeUntil. In its implementation we see a line like this
innerFrom(notifier).subscribe(new OperatorSubscriber(subscriber, () => subscriber.complete(), noop));
which, in essence, says that as soon as the notifier (i.e. the Observable passed to takeUntil as parameter) notifies, the complete method is called on the subscriber.
The invocation of the complete method triggers many things, but eventually it ends up calling the method execTeardown of Subscription which ends up invoking unsubscribe of OperatorSubscriber which itself calls unsubscribe of Subscription.
As we see, the chain is pretty long and complex to follow, but the core message is that the tearDown logic (i.e. the logic which is invoked when an Observable completes, errors or is unsubscribed) calls the unsubscription logic.
Maybe it is useful to look at one more thing, an implementation of a custom operator directly from the RxJs documentation.
In this case, at the end of the definition of the operator, we find this piece of code
// Return the teardown logic. This will be invoked when
// the result errors, completes, or is unsubscribed.
return () => {
subscription.unsubscribe();
// Clean up our timers.
for (const timerID of allTimerIDs) {
clearTimeout(timerID);
}
};
This is the teardown logic for this custom operator and such logic invokes the unsubscribe as well as any other cleanup activity.

Why is there no onComplete() rxjs operator?

Why rxjs contains no onComplete operator, i.e., one that allows to do something when the source observable completes? The finalize operator works for both completion and error, while I need to only react to completion.
Yes, I know that I can use the onComplete callback of the subscribe() function, but this is something completely different. Sometimes you only want to do certain stuff on completion inside the rxjs pipeline, and not inside the subscribe call.
Am I missing something?
The tap operator accepts three arguments (value, error, completion)
ons$.pipe(
tap(null, null, () => console.log("Done")),
).subscribe()
or alternatively an observer, so you can do this:
obs$.pipe(
tap({ complete: () => console.log("Done") })
).subscribe()
See the operator docs for more: https://rxjs-dev.firebaseapp.com/api/operators/tap
I would recommend the observer syntax as this is the syntax that has been recommended by Ben Lesh in the past since it's more expressive.

Does RxJs .first() operator (among others) complete the source observable?

If I have the following code:
const subject = new BehaviorSubject<[]>([]);
const observable = subject.asObservable();
subject.next([{color: 'blue'}])
observable.pipe(first()).subscribe(v => console.log(v))
According to the docs:
If called with no arguments, first emits the first value of the source Observable, then completes....
Does this mean that the source observable(the BehaviorSubject in this case) completes and you can no longer use it? As in you can no longer call .next([...]) on it.
I'm trying to understand how can an observable complete if it doesnt have the .complete() method on it?
I was trying to look at the source code of first() which under the covers uses take() and in turn take() uses lift() so I was curious if somehow first operator returns a copy of the source observable(the subject) and completes that.
The source observable is not completing, what it completes is the subscription. You could have multiple subscriptions on your Observable source, in your case one BehaviorSubject.
subject.next([{color: 'blue'}])
subject.next([{color: 'red'}])
const subs1 = observable.pipe(first()).subscribe(v => console.log(v))
const subs2 = observable.subscribe(v => console.log(v))
In the example above you clearly see that the source is not completing, just the subscription.
I have created a Stackblitz if you want to try it: https://stackblitz.com/edit/rxjs-uv6h6i
Hope I got your point!
Cheers :)

RXJS repeat does not have a chance to repeat?

I have the following epic I use in my application to handle api requests:
action$ => {
return action$.ofType(actions.requestType)
.do(() => console.log('handled epic ' + actions.requestType))
.switchMap((action) => (
Observable.create((obs) => {
obs.next({ type: type, value: action.value, form: action.form });
})
.debounceTime(250)
.switchMap((iea) => (
Observable.ajax(ajaxPost(url(iea.value), body ? body(iea.value) : action.form))
.mergeMap(payload => {
return Observable.merge(
Observable.of(actions.success(payload)),
/* some other stuff */
);
})
.catch(payload => {
return [actions.failure(payload)];
})
))
))
.takeUntil(action$.filter((a) => (a.type === masterCancelAction))
.repeat();
};
Basically, any time I perform an api request, I dispatch a request action. If I dispatch another request quickly, the previous one is ignored using debounceTime. Additionally, the request can be cancelled using the masterCancelAction and when cancelled repeat() restarts the epic. This epic works as intended in all cases expect one.
The failure case occurs when a user uses the browser back during a request. In this case I fire the masterCancelAction to the request. However, on the same execution context as a result from the masterCancelAction, another request action dispatches to perform a new request on the same epic, but the api request does not occur (the console.log does occur though) as if there was no repeat(). In other cases where cancels occur, the next request is not invoked from the same execution context and it works fine, so it seems in this case my code does not give repeat a chance to restart the epic?
A dirty workaround I found was to use setTimeout(dispatch(action), 0) on the request that dispatches after the cancellation. This seems to allow repeat() to execute. I tried passing different schedulers into repeat, but that didn't seem to help. Also, attaching takeUntil and repeat into my inner switchMap solves the problem, but then other cases where my next request does not execute in the same call stack fail.
Is there a way I can solve this problem without using setTimeout? Maybe it is not a repeat related problem, but it seems to be the case.
Using rxjs 5.0.3 and redux-observable 0.14.1.
The issue is not 100% clear without something like a jsbin to see what you mean, but I do see some general issues that might help:
Anonymous Observable never completes
When creating a custom anonymous Observable it's important to call observer.complete() if you do indeed want it to complete. In most cases, not doing so will cause the subscription to be a memory leak and might also other strange behaviors
Observable.create((observer) => {
observer.next({ type: type, value: action.value, form: action.form });
observer.complete();
})
Observable.of would have been equivalent:
Observable.of({ type: type, value: action.value, form: action.form })
However, it's not clear why this was done as the values it emits are in captured in scope.
debounceTime in this case does not debounce, it delays
Since the anonymous observable it's applied to only ever emits a single item, debounceTime will act just as a regular .delay(250). I'm betting you intended instead to debounce actions.requestType actions, in which case you'd need to apply your debouncing outside the switchMap, after the action$.ofType(actions.requestType).
Observable.of accepts any number of arguments to emit
This is more of a "did you know?" rather than an issue, but I noticed you're merging your of and /* some other actions */ I assume would be other of observables merged in. Instead, you can just return a single of and pass the actions as arguments.
Observable.of(
actions.success(payload),
/* some other actions */
actions.someOtherOne(),
actions.etc()
);
Also, when you find yourself emitting multiple actions synchronously like this, consider whether your reducers should be listening for the same, single action instead of having two or more. Sometimes this wouldn't make sense as you want them to have completely unrelated actions, just something to keep in mind that people often forget--that all reducers receive all actions and so multiple reducers can change their state from the same action.
.takeUntil will stop the epic from listening for future actions
Placing the takeUntil on the top-level observable chain causes the epic to stop listening for action$.ofType(actions.requestType), which is why you added the .repeat() after. This might work in some cases, but it's inefficient and can cause other hard to realize bugs. Epics should be thought of instead as sort of like sidecar processes that usually "start up" with the app and then continue listening for a particular action until the app "shuts down" aka the user leaves the app. They aren't actually processes, it's just helpful to conceptually think of them this way as an abstraction.
So each time it matches its particular action it then most often will switchMap, mergeMap, concatMap, or exhaustMap into some side effect, like an ajax call. That inner observable chain is what you want to make cancellable. So you'd place your .takeUntil on it, at the appropriate place in the chain.
Summary
As mentioned, it's not clear what you intended to do and what the issue is, without a more complete example like a jsbin. But strictly based on the code provided, this is my guesstimate:
const someRequestEpic = action$ => {
return action$.ofType(actions.requestType)
.debounceTime(250)
.do(() => console.log('handled epic ' + actions.requestType))
.switchMap((action) =>
Observable.ajax(ajaxPost(url(action.value), body ? body(action.value) : action.form))
.takeUntil(action$.ofType(masterCancelAction))
.mergeMap(payload => {
return Observable.of(
actions.success(payload),
/* some other actions */
...etc
);
})
.catch(payload => Observable.of(
actions.failure(payload)
))
);
};
Check out the Cancellation page in the redux-observable docs.
If this is a bit confusing, I'd recommend digging a bit deeper into what Observables are and what an "operator" is and does so that it doesn't feel magical and where you should place an operator makes more sense.
Ben's post on Learning Observable by Building Observable is a good start.

Does CreateFromObservable works asynchrony?

I'm trying use ReactiveUI 7.4 in WPF project, and I think it's great framework. But it causes great difficulties in studying the absence, or the outdated documentation.
In doc https://docs.reactiveui.net/en/user-guide/commands/asynchronous-synchronous.html says so CreateFromObservable is asynchrony, but in my example it's run syncronly.
RefreshList = ReactiveCommand.CreateFromObservable<ReactiveList<ClientDto>>(
() => Observable
.Return(_clientsService.GetClientsList())
and latter
_isBusy = this.WhenAnyObservable(x => x.RefreshList.IsExecuting)
.ToProperty(this, vm => vm.IsBusy);
when i do InvokeCommand method runs syncronly, and IsExecuting observe only after GetClientsList() completed (change to false and after to true). But variand with task works:
RefreshList = ReactiveCommand.CreateFromTask(async _ =>
{
return await Task.Run(() => _clientsService.GetClientsList());
}
);
Is it bug? Or changes in framework?
PS I also trying plays with SubscribeOn and ObservableOn but nothing helps (((.
Observable.Return() does it's work on the current thread, which means it's blocking. In your case the current thread is the UI thread.
You can specify a scheduler, but that only affects where the value is returned, not where it's produced.
I've written about Observable.Return() and how it behaves in this blog post.
It looks like _clientsService.GetClientsList() is implemented synchronously. To make it asynchronous you can move the work to the task pool. You've already done this by running it in a Task. It's also possible to use `Observable.Start()ยด:
RefreshList = ReactiveCommand.CreateFromObservable<ReactiveList<ClientDto>>(
() => Observable
.Start(_clientsService.GetClientsList(), RxApp.TaskpoolScheduler);

Resources