Entry No. 16

Proper way to implement ReactiveCommand.

What is it?

ReactiveCommand is a Reactive Extensions and asynchronous aware implementation of the ICommand interface and can be executed either synchronously or asynchronously.

What is it for?

It turns a method into an observable. Hence, we can bind it to any event, can observe it, get its result and do anything further.

Creating

var canExecute = this.WhenAnyValue(vm => vm.CanExecute); // set condition
var command = ReactiveCommand.Create<TInput, TOutput>(Method, canExecute); // create command
command.ThrownExceptions.Subscribe(error => { }); // catch exceptions

Executing

//1. Just execute it
command.Execute().Subscribe(); 
// observable returned by Execute is cold, so we need to Subscribe to let it execute

//2. Check canExecute before execute
observable.InvokeCommand(command);

//3. Execute command in a task
await command.Execute();

There are many ways to execute a method, we can directly execute it or turn it into a command. Furthermore, the command also has many ways to let us execute it.

Hence, we need to know what we expect from the method:
  • If it is just a method in view model, view doesn’t care about it, we just directly execute it.
  • If the view need to observe it:
    1. For fire and forget: use “1” from Executing part
    2. For a command that needs checking use “2” from Executing part
    3. There is a sequence of commands like: A -> B -> C -> D and View only care about A and D we need to call B inside A by using “3” from Executing part

Sample code of implementation for a set of commands that need to be run in order: A -> B -> C -> D:

var commandA = ReactiveCommand.CreateFromTask(DoA);
var commandB = ReactiveCommand.CreateFromTask(DoB);
var commandC = ReactiveCommand.CreateFromTask(DoC);
var commandD = ReactiveCommand.CreateFromTask(DoD);

private async Task DoA()
{
    // do things
    await CommandB.Execute();
}

private async Task DoB()
{
    // do things
    await CommandC.Execute();
}

private async Task DoC()
{
    // do things
    await CommandD.Execute();
}

private async Task DoD()
{
    // do things
}
Performance in order from fastest to slowest:
  1. Directly call
  2. Await Execute()
  3. Execute().Subcribe()
  4. Invoke

Side Effects:

Actually, the binding is not real real-time like we think it is.

Just check out the following code:

command 2 and command 3 are supposed to be called after command 1 finished.

But, the result is not what we expected:

Command 2 was not called, why?
  • As we can see, command 2 and command 3 only can be executed when command 1 is finished.
  • When command 1 is finished, its IsExecuting changed to False to enable command 2 and command 3. But, at that time, this IsExecuting was not bound to CanExecute, so it won’t let us execute command2.
  • For command3, we got the Delay, after a short time (likes 100ms), everything was bound and had the expected value, so the command3 can be executed.
So, to avoid it, we have 3 options:
  1. Use delay (which is the worst).
  2. Use directly IsExecuting from the command without any binding.
  3. Call the needed command inside the executing command. (Recommended)