CommunityToolkit / CommunityToolkit/dotnet

Non-cancelling, non-concurrent AsyncRelayCommand

Open
#1,125 1 comment 0 reactions 0 assignees View on GitHub
feature request :mailbox_with_mail:
Dominant language
C#
Stars
3.8k
Forks
400
PR merge metrics
No merged PRs in 30d

Description

### Overview

There are contexts where if an async command is executed for any reason while an execution of the command is already in progress, we don't want to cancel the in-progress execution and start over. It would be useful, therefore, to add the ability to set an option for AsyncRelayCommand (and AsyncRelayCommand) that skips the normal behaviour of ExecuteAsync() when an execution is already ongoing.

### API breakdown

```csharp
namespace CommunityToolkit.Mvvm.Input;

[Flags]
public enum AsyncRelayCommandOptions
{
/// ... existing members ...

///
/// Concurrent executions are disallowed. This options makes it so that the same command cannot be invoked while it is already executing.
///
/// Note that additional considerations should be taken into account in this case:
///
/// If the command supports cancellation, the current invocation can still be canceled before starting a new one.
/// The property will always represent the current operation.
/// This option and are mutually exclusive.
///
///
///
ContinueExistingExecutions = 1 << 2
}

public sealed class RelayCommandAttribute : Attribute
{
/// ... existing members ...

///
/// Gets or sets a value indicating whether or not to ignore concurrent execution attempts for an asynchronous command.
///
/// When set for an attribute used on a method that would result in a or an
/// property to be generated, this will modify the behavior of these commands
/// when an execution is invoked while a previous one is still running. It is the same as creating an instance of
/// these command types with a constructor such as
/// and using the value.
///
///
///
/// Using this property is not valid if the target command doesn't map to an asynchronous command.
/// Using this property in conjunction with is not supported.
///
public bool ContinueExistingExecutions { get; init; }
}
```

### Usage example

```csharp
class ViewModel
{
[RelayCommand(ContinueExistingExecutions = true)]
private Task PerformNonRestartableOperationAsync(CancellationToken cancellationToken = default)
{
// do something that shouldn't be restarted whenever the command is invoked
}
}
```

### Breaking change?

I'm not sure

### Alternatives

As an alternative I've created modified `AsyncRelayCommandAlt` and `AsyncRelayCommandAlt` classes in our project based on the MVVM Toolkit implementations, but without the checks for the `AllowConcurrentExecutions` option and with the following changes at the start of the ExecuteAsync methods:

```csharp
public Task ExecuteAsync(object? parameter)
{
// If we're already running, no-op out
if (ExecutionTask is { IsCompleted: false })
return Task.Completed;

// ... existing implementation ...
}
```

This is far less convenient however as it means we can't take advantage of code generation through RelayCommandAttribute.

### Additional context

While I've used the name `ContinueExistingExecutions` I think it's a pretty bad name, I just couldn't think of anything better right now. I'd be very happy if someone could suggest a better name whether or not this proposal gets accepted.

### Help us help you

Yes, but only if others can assist

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.