{"id":24745503,"url":"https://github.com/TheCodeTraveler/AsyncAwaitBestPractices","last_synced_at":"2025-10-10T13:31:55.974Z","repository":{"id":28527528,"uuid":"118641651","full_name":"TheCodeTraveler/AsyncAwaitBestPractices","owner":"TheCodeTraveler","description":"Extensions for System.Threading.Tasks.Task and System.Threading.Tasks.ValueTask","archived":false,"fork":false,"pushed_at":"2025-09-30T18:01:47.000Z","size":1059,"stargazers_count":1756,"open_issues_count":3,"forks_count":172,"subscribers_count":49,"default_branch":"main","last_synced_at":"2025-10-05T02:54:55.818Z","etag":null,"topics":["async","await","best-practices","icommand","task","threading"],"latest_commit_sha":null,"homepage":"https://www.nuget.org/packages/AsyncAwaitBestPractices.MVVM/","language":"C#","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/TheCodeTraveler.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":".github/FUNDING.yml","license":"LICENSE.md","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null},"funding":{"github":["brminnick"]}},"created_at":"2018-01-23T17:03:56.000Z","updated_at":"2025-10-01T19:37:19.000Z","dependencies_parsed_at":"2024-01-13T16:25:32.727Z","dependency_job_id":"76e6c85d-fd3f-4ff1-86a6-7e4e7ef0c37d","html_url":"https://github.com/TheCodeTraveler/AsyncAwaitBestPractices","commit_stats":{"total_commits":511,"total_committers":15,"mean_commits":34.06666666666667,"dds":0.2857142857142857,"last_synced_commit":"42633db95b3a426beba5f3cd310de9182bf8c59a"},"previous_names":["thecodetraveler/asyncawaitbestpractices","brminnick/asyncawaitbestpractices"],"tags_count":49,"template":false,"template_full_name":null,"purl":"pkg:github/TheCodeTraveler/AsyncAwaitBestPractices","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TheCodeTraveler%2FAsyncAwaitBestPractices","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TheCodeTraveler%2FAsyncAwaitBestPractices/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TheCodeTraveler%2FAsyncAwaitBestPractices/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TheCodeTraveler%2FAsyncAwaitBestPractices/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/TheCodeTraveler","download_url":"https://codeload.github.com/TheCodeTraveler/AsyncAwaitBestPractices/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/TheCodeTraveler%2FAsyncAwaitBestPractices/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":279004062,"owners_count":26083667,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-10-10T02:00:06.843Z","response_time":62,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["async","await","best-practices","icommand","task","threading"],"created_at":"2025-01-28T03:02:00.916Z","updated_at":"2025-10-10T13:31:55.967Z","avatar_url":"https://github.com/TheCodeTraveler.png","language":"C#","readme":"# AsyncAwaitBestPractices\n\n[![Build Status](https://brminnick.visualstudio.com/AsyncAwaitBestPractices/_apis/build/status/brminnick.AsyncAwaitBestPractices?branchName=main)](https://brminnick.visualstudio.com/AsyncAwaitBestPractices/_build/latest?definitionId=8\u0026branchName=main)\n\nExtensions for `System.Threading.Tasks.Task`.\n\nInspired by [John Thiriet](https://github.com/johnthiriet)'s blog posts: \n- [Removing Async Void](https://johnthiriet.com/removing-async-void/)\n- [MVVM - Going Async With AsyncCommand](https://johnthiriet.com/mvvm-going-async-with-async-command/).\n\n###  AsyncAwaitBestPractices\n\n[![NuGet Version](https://img.shields.io/nuget/vpre/AsyncAwaitBestPractices)](https://www.nuget.org/packages/AsyncAwaitBestPractices/)\n [![NuGet](https://img.shields.io/nuget/dt/AsyncAwaitBestPractices)](https://www.nuget.org/packages/AsyncAwaitBestPractices/)\n\nAvailable on NuGet: https://www.nuget.org/packages/AsyncAwaitBestPractices/ \n\n- `SafeFireAndForget`\n    - An extension method to safely fire-and-forget a `Task` or a `ValueTask`\n    - Ensures the `Task` will rethrow an `Exception` if an `Exception` is caught in `IAsyncStateMachine.MoveNext()`\n- `WeakEventManager`\n    - Avoids memory leaks when events are not unsubscribed\n    - Used by `AsyncCommand`, `AsyncCommand\u003cT\u003e`, `AsyncValueCommand`, `AsyncValueCommand\u003cT\u003e`\n- [Usage instructions](#asyncawaitbestpractices-3)\n  \n### AsyncAwaitBestPractices.MVVM\n\n[![NuGet Version](https://img.shields.io/nuget/vpre/AsyncAwaitBestPractices.MVVM)](https://www.nuget.org/packages/AsyncAwaitBestPractices.MVVM/)\n [![NuGet](https://img.shields.io/nuget/dt/AsyncAwaitBestPractices.MVVM)](https://www.nuget.org/packages/AsyncAwaitBestPractices.MVVM/)\n\n- Available on NuGet: https://www.nuget.org/packages/AsyncAwaitBestPractices.MVVM/  \n\n- Allows for `Task` to safely be used asynchronously with `ICommand`:\n  - `IAsyncCommand : ICommand`\n  - `AsyncCommand : IAsyncCommand`\n  - `IAsyncCommand\u003cT\u003e : ICommand`    \n  - `AsyncCommand\u003cT\u003e : IAsyncCommand\u003cT\u003e`\n  - `IAsyncCommand\u003cTExecute, TCanExecute\u003e : IAsyncCommand\u003cTExecute\u003e`    \n  - `AsyncCommand\u003cTExecute, TCanExecute\u003e : IAsyncCommand\u003cTExecute, TCanExecute\u003e`    \n  \n- Allows for `ValueTask` to safely be used asynchronously with `ICommand`:\n  - `IAsyncValueCommand : ICommand`\n  - `AsyncValueCommand : IAsyncValueCommand`\n  - `IAsyncValueCommand\u003cT\u003e : ICommand`    \n  - `AsyncValueCommand\u003cT\u003e : IAsyncValueCommand\u003cT\u003e`\n  - `IAsyncValueCommand\u003cTExecute, TCanExecute\u003e : IAsyncValueCommand\u003cTExecute\u003e`    \n  - `AsyncValueCommand\u003cTExecute, TCanExecute\u003e : IAsyncValueCommand\u003cTExecute, TCanExecute\u003e`   \n- [Usage instructions](#asyncawaitbestpracticesmvvm-2)\n\n## Setup\n\n###  AsyncAwaitBestPractices\n\n- Available on NuGet: https://www.nuget.org/packages/AsyncAwaitBestPractices/ \n- Add to any project supporting .NET Standard 1.0\n\n### AsyncAwaitBestPractices.MVVM\n\n- Available on NuGet: https://www.nuget.org/packages/AsyncAwaitBestPractices.MVVM/  \n- Add to any project supporting .NET Standard 1.0\n\n## Why Do I Need This?\n\n### Courses\n\nJoin me in this 4 Hour [DomeTrain](https://dometrain.com) course where we'll learn everything you need to know to master asynchronous programming using async await in C# and .NET\n\n[\u003cimg width=\"750\" alt=\"Screenshot 2025-02-24 at 10 14 11 AM\" src=\"https://github.com/user-attachments/assets/41e3a803-7a8b-4979-8948-71773e3db2b6\" /\u003e](https://dometrain.com/course/from-zero-to-hero-asynchronous-programming-in-csharp/) [\u003cimg width=\"250\" alt=\"Screenshot 2025-02-24 at 10 14 15 AM\" src=\"https://github.com/user-attachments/assets/e3ffb431-23fe-4e50-9d33-679f5af3725d\" /\u003e](https://dometrain.com/course/from-zero-to-hero-asynchronous-programming-in-csharp/?srsltid=AfmBOor-HQ1ZO6WzeTya15Cp1K_iBxnrWxmqVRhI_gbzjbbJWt1_c_nI)\n\n\n\n### Podcasts\n\n[No Dogma Podcast](https://nodogmapodcast.bryanhogan.net), Hosted by [Bryan Hogan](https://twitter.com/bryanjhogan)\n- [Episode #133 Brandon Minnick, Async Await – Common Mistakes, Part 1](https://nodogmapodcast.bryanhogan.net/133-brandon-minnick-async-await-common-mistakes-part-1/)\n- [Episode #134 Brandon Minnick, Async Await – Common Mistakes, Part 2](https://nodogmapodcast.bryanhogan.net/134-brandon-minnick-async-await-common-mistakes-part-2/)\n\n### Video\n\n**NDC Melbourne 2025**\n\n[Correcting Common Async Await Mistakes in .NET 9](https://www.youtube.com/watch?v=6frfLI3HqKI)\n\n[![](https://github.com/user-attachments/assets/849ef8db-354b-4223-9dc9-09263671f6e3)](https://www.youtube.com/watch?v=6frfLI3HqKI)\n\n\n### Explanation\n\nAsync/await is great *but* there are two subtle problems that can easily creep into code:\n1. Creating race conditions/concurrent execution (where you code things in the right order but the code executes in a different order than you expect) \n2. Creating methods where the compiler recognizes exceptions but you the coder never see them (making it head-scratchingly annoying to debug *especially* if you accidentally introduced a race condition that you can’t see).\n\nThis library solves both of these problems.\n\nTo better understand why this library was created and the problem it solves, it’s important to first understand how the compiler generates code for an async method.  \n\n**tl;dr** A non-awaited `Task` doesn't rethrow exceptions and `AsyncAwaitBestPractices.SafeFireAndForget` ensures it will\n\n## Compiler-Generated Code for Async Method\n\n![Compiler-Generated Code for Async Method](https://i.stack.imgur.com/c9im1.png)\n\n(Source: [Xamarin University: _Using Async and Await_](https://github.com/XamarinUniversity/CSC350))\n\nThe compiler transforms an `async` method into an `IAsyncStateMachine` class which allows the .NET Runtime to \"remember\" what the method has accomplished.\n\n![Move Next](https://i.stack.imgur.com/JsmG1.png)\n\n(Source: [Xamarin University: _Using Async and Await_](https://github.com/XamarinUniversity/CSC350))\n\nThe `IAsyncStateMachine` interface implements `MoveNext()`, a method the executes every time the `await` operator is used inside of the `async` method.\n\n`MoveNext()` essentially runs your code until it reaches an `await` statement, then it `return`s while the `await`'d method executes. This is the mechanism that allows the current method to \"pause\", yielding its thread execution to another thread/Task.\n\n### Try/Catch in `MoveNext()`\n\nLook closely at `MoveNext()`; notice that it is wrapped in a `try/catch` block.\n\nBecause the compiler creates `IAsyncStateMachine` for every `async` method and `MoveNext()` is _always_ wrapped in a `try/catch`, every exception thrown inside of an `async` method is caught!\n\n## How to Rethrow an Exception Caught By `MoveNext`\n\nNow we see that the `async` method catches every exception thrown - that is to say, the exception is caught internally by the state machine, *but* you the coder will not see it.  In order for you to see it, you'll need to rethrow the exception to surface it in your debugging.  So the questions is - how do I rethrow the exception? \n\nThere are a few ways to rethrow exceptions that are thrown in an `async` method:\n\n 1. Use the `await` keyword _(Prefered)_\n    - e.g. `await DoSomethingAsync()`\n 2. Use `.GetAwaiter().GetResult()`\n    - e.g. `DoSomethingAsync().GetAwaiter().GetResult()`\n\nThe `await` keyword is preferred because `await` allows the `Task` to run asynchronously on a different thread, and it will not lock-up the current thread.\n\n### What About `.Result` or `.Wait()`?\n\nNever, never, never, never, never use `.Result` or `.Wait()`:\n\n1. Both `.Result` and `.Wait()` will lock-up the current thread. If the current thread is the Main Thread (also known as the UI Thread), your UI will freeze until the `Task` has completed.\n\n2. `.Result` or `.Wait()` rethrow your exception as a `System.AggregateException`, which makes it difficult to find the actual exception.\n  \n# Usage\n\n## AsyncAwaitBestPractices\n\n### `SafeFireAndForget`\nAn extension method to safely fire-and-forget a `Task`.\n\n`SafeFireAndForget` allows a Task to safely run on a different thread while the calling thread does not wait for its completion.\n\n```csharp\npublic static async void SafeFireAndForget(this System.Threading.Tasks.Task task, System.Action\u003cSystem.Exception\u003e? onException = null, bool continueOnCapturedContext = false)\n```\n\n```csharp\npublic static async void SafeFireAndForget(this System.Threading.Tasks.ValueTask task, System.Action\u003cSystem.Exception\u003e? onException = null, bool continueOnCapturedContext = false)\n```\n\n#### On .NET 8.0 (and higher)\n\n.NET 8.0 Introduces [`ConfigureAwaitOptions`](https://learn.microsoft.com/dotnet/api/system.threading.tasks.configureawaitoptions) that allow users to customize the behavior when awaiting:\n- `ConfigureAwaitOptions.None`\n    - No options specified\n- `ConfigureAwaitOptions.SuppressThrowing`\n    - Avoids throwing an exception at the completion of awaiting a Task that ends in the Faulted or Canceled state\n- `ConfigureAwaitOptions.ContinueOnCapturedContext`\n    - Attempts to marshal the continuation back to the original SynchronizationContext or TaskScheduler present on the originating thread at the time of the await\n- `ConfigureAwaitOptions.ForceYielding`\n    - Forces an await on an already completed Task to behave as if the Task wasn't yet completed, such that the current asynchronous method will be forced to yield its execution\n\nFor more information, check out Stephen Cleary's blog post, [\"ConfigureAwait in .NET 8\"](https://blog.stephencleary.com/2023/11/configureawait-in-net-8.html).\n\n```csharp\npublic static void SafeFireAndForget(this System.Threading.Tasks.Task task, ConfigureAwaitOptions configureAwaitOptions, Action\u003cException\u003e? onException = null)\n```\n\n#### Basic Usage - Task\n\n```csharp\nvoid HandleButtonTapped(object sender, EventArgs e)\n{\n    // Allows the async Task method to safely run on a different thread while the calling thread continues, not awaiting its completion\n    // onException: If an Exception is thrown, print it to the Console\n    ExampleAsyncMethod().SafeFireAndForget(onException: ex =\u003e Console.WriteLine(ex));\n\n    // HandleButtonTapped continues execution here while `ExampleAsyncMethod()` is running on a different thread\n    // ...\n}\n\nasync Task ExampleAsyncMethod()\n{\n    await Task.Delay(1000);\n}\n```\n\n\u003e **Note:** `ConfigureAwaitOptions.SuppressThrowing` will always supress exceptions from being rethrown. This means that `onException` will never execute when `ConfigureAwaitOptions.SuppressThrowing` is set.\n\n#### Basic Usage - ValueTask\n\nIf you're new to ValueTask, check out this great write-up, [Understanding the Whys, Whats, and Whens of ValueTask\n](https://blogs.msdn.microsoft.com/dotnet/2018/11/07/understanding-the-whys-whats-and-whens-of-valuetask?WT.mc_id=mobile-0000-bramin).\n\n```csharp\nvoid HandleButtonTapped(object sender, EventArgs e)\n{\n    // Allows the async ValueTask method to safely run on a different thread while the calling thread continues, not awaiting its completion\n    // onException: If an Exception is thrown, print it to the Console\n    ExampleValueTaskMethod().SafeFireAndForget(onException: ex =\u003e Console.WriteLine(ex));\n\n    // HandleButtonTapped continues execution here while `ExampleAsyncMethod()` is running on a different thread\n    // ...\n}\n\nasync ValueTask ExampleValueTaskMethod()\n{\n    var random = new Random();\n    if (random.Next(10) \u003e 9)\n        await Task.Delay(1000);\n}\n```\n\n#### Advanced Usage\n\n```csharp\nvoid InitializeSafeFireAndForget()\n{\n    // Initialize SafeFireAndForget\n    // Only use `shouldAlwaysRethrowException: true` when you want `.SafeFireAndForget()` to always rethrow every exception. This is not recommended, because there is no way to catch an Exception rethrown by `SafeFireAndForget()`; `shouldAlwaysRethrowException: true` should **not** be used in Production/Release builds.\n    SafeFireAndForgetExtensions.Initialize(shouldAlwaysRethrowException: false);\n\n    // SafeFireAndForget will print every exception to the Console\n    SafeFireAndForgetExtensions.SetDefaultExceptionHandling(ex =\u003e Console.WriteLine(ex));\n}\n\nvoid UninitializeSafeFireAndForget()\n{\n    // Remove default exception handling\n    SafeFireAndForgetExtensions.RemoveDefaultExceptionHandling();\n}\n\nvoid HandleButtonTapped(object sender, EventArgs e)\n{\n    // Allows the async Task method to safely run on a different thread while not awaiting its completion\n    // onException: If a WebException is thrown, print its StatusCode to the Console. **Note**: If a non-WebException is thrown, it will not be handled by `onException`\n    // Because we set `SetDefaultExceptionHandling` in `void InitializeSafeFireAndForget()`, the entire exception will also be printed to the Console\n    ExampleAsyncMethod().SafeFireAndForget\u003cWebException\u003e(onException: ex =\u003e\n    {\n        if(ex.Response is HttpWebResponse webResponse)\n            Console.WriteLine($\"Task Exception\\n Status Code: {webResponse.StatusCode}\");\n    });\n    \n    ExampleValueTaskMethod().SafeFireAndForget\u003cWebException\u003e(onException: ex =\u003e\n    {\n        if(ex.Response is HttpWebResponse webResponse)\n            Console.WriteLine($\"ValueTask Error\\n Status Code: {webResponse.StatusCode}\");\n    });\n\n    // HandleButtonTapped continues execution here while `ExampleAsyncMethod()` and `ExampleValueTaskMethod()` run in the background\n}\n\nasync Task ExampleAsyncMethod()\n{\n    await Task.Delay(1000);\n    throw new WebException();\n}\n\nasync ValueTask ExampleValueTaskMethod()\n{\n    var random = new Random();\n    if (random.Next(10) \u003e 9)\n        await Task.Delay(1000);\n        \n    throw new WebException();\n}\n```\n\n\u003e **Note:** `ConfigureAwaitOptions.SuppressThrowing` will always supress exceptions from being rethrown. This means that `onException` will never execute when `ConfigureAwaitOptions.SuppressThrowing` is set.\n\n### `WeakEventManager`\n\nAn event implementation that enables the [garbage collector to collect an object without needing to unsubscribe event handlers](http://paulstovell.com/blog/weakevents).\n\nInspired by [Xamarin.Forms.WeakEventManager](https://github.com/xamarin/Xamarin.Forms/blob/master/Xamarin.Forms.Core/WeakEventManager.cs).\n\n#### Using `EventHandler`\n\n```csharp\nreadonly WeakEventManager _canExecuteChangedEventManager = new WeakEventManager();\n\npublic event EventHandler CanExecuteChanged\n{\n    add =\u003e _canExecuteChangedEventManager.AddEventHandler(value);\n    remove =\u003e _canExecuteChangedEventManager.RemoveEventHandler(value);\n}\n\nvoid OnCanExecuteChanged() =\u003e _canExecuteChangedEventManager.RaiseEvent(this, EventArgs.Empty, nameof(CanExecuteChanged));\n```\n\n#### Using `Delegate`\n\n```csharp\nreadonly WeakEventManager _propertyChangedEventManager = new WeakEventManager();\n\npublic event PropertyChangedEventHandler PropertyChanged\n{\n    add =\u003e _propertyChangedEventManager.AddEventHandler(value);\n    remove =\u003e _propertyChangedEventManager.RemoveEventHandler(value);\n}\n\nvoid OnPropertyChanged([CallerMemberName]string propertyName = \"\") =\u003e _propertyChangedEventManager.RaiseEvent(this, new PropertyChangedEventArgs(propertyName), nameof(PropertyChanged));\n```\n\n#### Using `Action`\n\n```csharp\nreadonly WeakEventManager _weakActionEventManager = new WeakEventManager();\n\npublic event Action ActionEvent\n{\n    add =\u003e _weakActionEventManager.AddEventHandler(value);\n    remove =\u003e _weakActionEventManager.RemoveEventHandler(value);\n}\n\nvoid OnActionEvent(string message) =\u003e _weakActionEventManager.RaiseEvent(message, nameof(ActionEvent));\n```\n\n### `WeakEventManager\u003cT\u003e`\nAn event implementation that enables the [garbage collector to collect an object without needing to unsubscribe event handlers](http://paulstovell.com/blog/weakevents).\n\nInspired by [Xamarin.Forms.WeakEventManager](https://github.com/xamarin/Xamarin.Forms/blob/master/Xamarin.Forms.Core/WeakEventManager.cs).\n\n#### Using `EventHandler\u003cT\u003e`\n\n```csharp\nreadonly WeakEventManager\u003cstring\u003e _errorOcurredEventManager = new WeakEventManager\u003cstring\u003e();\n\npublic event EventHandler\u003cstring\u003e ErrorOcurred\n{\n    add =\u003e _errorOcurredEventManager.AddEventHandler(value);\n    remove =\u003e _errorOcurredEventManager.RemoveEventHandler(value);\n}\n\nvoid OnErrorOcurred(string message) =\u003e _errorOcurredEventManager.RaiseEvent(this, message, nameof(ErrorOcurred));\n```\n\n#### Using `Action\u003cT\u003e`\n\n```csharp\nreadonly WeakEventManager\u003cstring\u003e _weakActionEventManager = new WeakEventManager\u003cstring\u003e();\n\npublic event Action\u003cstring\u003e ActionEvent\n{\n    add =\u003e _weakActionEventManager.AddEventHandler(value);\n    remove =\u003e _weakActionEventManager.RemoveEventHandler(value);\n}\n\nvoid OnActionEvent(string message) =\u003e _weakActionEventManager.RaiseEvent(message, nameof(ActionEvent));\n```\n\n## AsyncAwaitBestPractices.MVVM\n\n### `AsyncCommand`\n\nAllows for `Task` to safely be used asynchronously with `ICommand`:\n\n- `AsyncCommand\u003cTExecute, TCanExecute\u003e : IAsyncCommand\u003cTExecute, TCanExecute\u003e`\n- `IAsyncCommand\u003cTExecute, TCanExecute\u003e : IAsyncCommand\u003cTExecute\u003e`\n- `AsyncCommand\u003cT\u003e : IAsyncCommand\u003cT\u003e`\n- `IAsyncCommand\u003cT\u003e : ICommand`\n- `AsyncCommand : IAsyncCommand`\n- `IAsyncCommand : ICommand`\n\n```csharp\npublic AsyncCommand(Func\u003cTExecute, Task\u003e execute,\n                     Func\u003cTCanExecute, bool\u003e? canExecute = null,\n                     Action\u003cException\u003e? onException = null,\n                     bool continueOnCapturedContext = false)\n```\n\n```csharp\npublic AsyncCommand(Func\u003cT, Task\u003e execute,\n                     Func\u003cobject?, bool\u003e? canExecute = null,\n                     Action\u003cException\u003e? onException = null,\n                     bool continueOnCapturedContext = false)\n```\n\n```csharp\npublic AsyncCommand(Func\u003cTask\u003e execute,\n                     Func\u003cobject?, bool\u003e? canExecute = null,\n                     Action\u003cException\u003e? onException = null,\n                     bool continueOnCapturedContext = false)\n```\n\n```csharp\npublic class ExampleClass\n{\n    bool _isBusy;\n\n    public ExampleClass()\n    {\n        ExampleAsyncCommand = new AsyncCommand(ExampleAsyncMethod);\n        ExampleAsyncIntCommand = new AsyncCommand\u003cint\u003e(ExampleAsyncMethodWithIntParameter);\n        ExampleAsyncIntCommandWithCanExecute = new AsyncCommand\u003cint, int\u003e(ExampleAsyncMethodWithIntParameter, CanExecuteInt);\n        ExampleAsyncExceptionCommand = new AsyncCommand(ExampleAsyncMethodWithException, onException: ex =\u003e Console.WriteLine(ex.ToString()));\n        ExampleAsyncCommandWithCanExecuteChanged = new AsyncCommand(ExampleAsyncMethod, _ =\u003e !IsBusy);\n        ExampleAsyncCommandReturningToTheCallingThread = new AsyncCommand(ExampleAsyncMethod, continueOnCapturedContext: true);\n    }\n\n    public IAsyncCommand ExampleAsyncCommand { get; }\n    public IAsyncCommand\u003cint\u003e ExampleAsyncIntCommand { get; }\n    public IAsyncCommand\u003cint, int\u003e ExampleAsyncIntCommandWithCanExecute { get; }\n    public IAsyncCommand ExampleAsyncExceptionCommand { get; }\n    public IAsyncCommand ExampleAsyncCommandWithCanExecuteChanged { get; }\n    public IAsyncCommand ExampleAsyncCommandReturningToTheCallingThread { get; }\n    \n    public bool IsBusy\n    {\n        get =\u003e _isBusy;\n        set\n        {\n            if (_isBusy != value)\n            {\n                _isBusy = value;\n                ExampleAsyncCommandWithCanExecuteChanged.RaiseCanExecuteChanged();\n            }\n        }\n    }\n\n    async Task ExampleAsyncMethod()\n    {\n        await Task.Delay(1000);\n    }\n  \n    async Task ExampleAsyncMethodWithIntParameter(int parameter)\n    {\n        await Task.Delay(parameter);\n    }\n\n    async Task ExampleAsyncMethodWithException()\n    {\n        await Task.Delay(1000);\n        throw new Exception();\n    }\n\n    bool CanExecuteInt(int count)\n    {\n        if(count \u003e 2)\n            return true;\n        \n        return false;\n    }\n\n    void ExecuteCommands()\n    {\n        _isBusy = true;\n    \n        try\n        {\n            ExampleAsyncCommand.Execute(null);\n            ExampleAsyncIntCommand.Execute(1000);\n            ExampleAsyncExceptionCommand.Execute(null);\n            ExampleAsyncCommandReturningToTheCallingThread.Execute(null);\n            \n            if(ExampleAsyncCommandWithCanExecuteChanged.CanExecute(null))\n                ExampleAsyncCommandWithCanExecuteChanged.Execute(null);\n            \n            if(ExampleAsyncIntCommandWithCanExecute.CanExecute(1))\n                ExampleAsyncIntCommandWithCanExecute.Execute(1);\n        }\n        finally\n        {\n            _isBusy = false;\n        }\n    }\n}\n```\n\n### `AsyncValueCommand`\n\nAllows for `ValueTask` to safely be used asynchronously with `ICommand`.\n\nIf you're new to ValueTask, check out this great write-up, [Understanding the Whys, Whats, and Whens of ValueTask\n](https://blogs.msdn.microsoft.com/dotnet/2018/11/07/understanding-the-whys-whats-and-whens-of-valuetask?WT.mc_id=mobile-0000-bramin).\n\n- `AsyncValueCommand\u003cTExecute, TCanExecute\u003e : IAsyncValueCommand\u003cTExecute, TCanExecute\u003e`\n- `IAsyncValueCommand\u003cTExecute, TCanExecute\u003e : IAsyncValueCommand\u003cTExecute\u003e`\n- `AsyncValueCommand\u003cT\u003e : IAsyncValueCommand\u003cT\u003e`\n- `IAsyncValueCommand\u003cT\u003e : ICommand`\n- `AsyncValueCommand : IAsyncValueCommand`\n- `IAsyncValueCommand : ICommand`\n\n```csharp\npublic AsyncValueCommand(Func\u003cTExecute, ValueTask\u003e execute,\n                            Func\u003cTCanExecute, bool\u003e? canExecute = null,\n                            Action\u003cException\u003e? onException = null,\n                            bool continueOnCapturedContext = false)\n```\n\n```csharp\npublic AsyncValueCommand(Func\u003cT, ValueTask\u003e execute,\n                            Func\u003cobject?, bool\u003e? canExecute = null,\n                            Action\u003cException\u003e? onException = null,\n                            bool continueOnCapturedContext = false)\n```\n\n```csharp\npublic AsyncValueCommand(Func\u003cValueTask\u003e execute,\n                            Func\u003cobject?, bool\u003e? canExecute = null,\n                            Action\u003cException\u003e? onException = null,\n                            bool continueOnCapturedContext = false)\n```\n\n```csharp\npublic class ExampleClass\n{\n    bool _isBusy;\n\n    public ExampleClass()\n    {\n        ExampleValueTaskCommand = new AsyncValueCommand(ExampleValueTaskMethod);\n        ExampleValueTaskIntCommand = new AsyncValueCommand\u003cint\u003e(ExampleValueTaskMethodWithIntParameter);\n        ExampleValueTaskIntCommandWithCanExecute = new AsyncValueCommand\u003cint, int\u003e(ExampleValueTaskMethodWithIntParameter, CanExecuteInt);\n        ExampleValueTaskExceptionCommand = new AsyncValueCommand(ExampleValueTaskMethodWithException, onException: ex =\u003e Debug.WriteLine(ex.ToString()));\n        ExampleValueTaskCommandWithCanExecuteChanged = new AsyncValueCommand(ExampleValueTaskMethod, _ =\u003e !IsBusy);\n        ExampleValueTaskCommandReturningToTheCallingThread = new AsyncValueCommand(ExampleValueTaskMethod, continueOnCapturedContext: true);\n    }\n\n    public IAsyncValueCommand ExampleValueTaskCommand { get; }\n    public IAsyncValueCommand\u003cint\u003e ExampleValueTaskIntCommand { get; }\n    public IAsyncCommand\u003cint, int\u003e ExampleValueTaskIntCommandWithCanExecute { get; }\n    public IAsyncValueCommand ExampleValueTaskExceptionCommand { get; }\n    public IAsyncValueCommand ExampleValueTaskCommandWithCanExecuteChanged { get; }\n    public IAsyncValueCommand ExampleValueTaskCommandReturningToTheCallingThread { get; }\n\n    public bool IsBusy\n    {\n        get =\u003e _isBusy;\n        set\n        {\n            if (_isBusy != value)\n            {\n                _isBusy = value;\n                ExampleValueTaskCommandWithCanExecuteChanged.RaiseCanExecuteChanged();\n            }\n        }\n    }\n\n    async ValueTask ExampleValueTaskMethod()\n    {\n        var random = new Random();\n        if (random.Next(10) \u003e 9)\n            await Task.Delay(1000);\n    }\n\n    async ValueTask ExampleValueTaskMethodWithIntParameter(int parameter)\n    {\n        var random = new Random();\n        if (random.Next(10) \u003e 9)\n            await Task.Delay(parameter);\n    }\n\n    async ValueTask ExampleValueTaskMethodWithException()\n    {\n        var random = new Random();\n        if (random.Next(10) \u003e 9)\n            await Task.Delay(1000);\n\n        throw new Exception();\n    }\n\n    bool CanExecuteInt(int count)\n    {\n        if(count \u003e 2)\n            return true;\n        \n        return false;\n    }\n\n    void ExecuteCommands()\n    {\n        _isBusy = true;\n\n        try\n        {\n            ExampleValueTaskCommand.Execute(null);\n            ExampleValueTaskIntCommand.Execute(1000);\n            ExampleValueTaskExceptionCommand.Execute(null);\n            ExampleValueTaskCommandReturningToTheCallingThread.Execute(null);\n\n            if (ExampleValueTaskCommandWithCanExecuteChanged.CanExecute(null))\n                ExampleValueTaskCommandWithCanExecuteChanged.Execute(null);\n\n            if(ExampleValueTaskIntCommandWithCanExecute.CanExecute(2))\n                ExampleValueTaskIntCommandWithCanExecute.Execute(2);\n        }\n        finally\n        {\n            _isBusy = false;\n        }\n    }\n}\n```\n\n## Learn More\n- [Removing Async Void](https://johnthiriet.com/removing-async-void/)\n- [MVVM Going Async with Async Command](https://johnthiriet.com/mvvm-going-async-with-async-command/)\n- [Asynchronous Programming in .NET](https://docs.microsoft.com/dotnet/csharp/async?WT.mc_id=mobile-0000-bramin)\n- [The Managed Thread Pool](https://docs.microsoft.com/dotnet/standard/threading/the-managed-thread-pool?WT.mc_id=mobile-0000-bramin)\n- [Understanding the Whys, Whats, and Whens of ValueTask](https://devblogs.microsoft.com/dotnet/understanding-the-whys-whats-and-whens-of-valuetask/?WT.mc_id=mobile-0000-bramin)\n- [Async/Await Best Practices Video](https://www.youtube.com/watch?v=yyT6dSjq-nE\u0026feature=youtu.be)\n- [What is Synchronization Context?](http://hamidmosalla.com/2018/06/24/what-is-synchronizationcontext/)\n","funding_links":["https://github.com/sponsors/brminnick"],"categories":["others","C\\#","C# #","C#"],"sub_categories":[],"project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FTheCodeTraveler%2FAsyncAwaitBestPractices","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FTheCodeTraveler%2FAsyncAwaitBestPractices","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FTheCodeTraveler%2FAsyncAwaitBestPractices/lists"}