SkillAgentSearch skills...

AsyncAwaitBestPractices

Extensions for System.Threading.Tasks.Task and System.Threading.Tasks.ValueTask

Install / Use

/learn @TheCodeTraveler/AsyncAwaitBestPractices
About this skill

Quality Score

0/100

Supported Platforms

Universal

README

AsyncAwaitBestPractices

Build Status

Extensions for System.Threading.Tasks.Task.

Inspired by John Thiriet's blog posts:

AsyncAwaitBestPractices

NuGet Version NuGet

Available on NuGet: https://www.nuget.org/packages/AsyncAwaitBestPractices/

  • SafeFireAndForget
    • An extension method to safely fire-and-forget a Task or a ValueTask
    • Ensures the Task will rethrow an Exception if an Exception is caught in IAsyncStateMachine.MoveNext()
  • WeakEventManager
    • Avoids memory leaks when events are not unsubscribed
    • Used by AsyncCommand, AsyncCommand<T>, AsyncValueCommand, AsyncValueCommand<T>
  • Usage instructions

AsyncAwaitBestPractices.MVVM

NuGet Version NuGet

  • Available on NuGet: https://www.nuget.org/packages/AsyncAwaitBestPractices.MVVM/

  • Allows for Task to safely be used asynchronously with ICommand:

    • IAsyncCommand : ICommand
    • AsyncCommand : IAsyncCommand
    • IAsyncCommand<T> : ICommand
    • AsyncCommand<T> : IAsyncCommand<T>
    • IAsyncCommand<TExecute, TCanExecute> : IAsyncCommand<TExecute>
    • AsyncCommand<TExecute, TCanExecute> : IAsyncCommand<TExecute, TCanExecute>
  • Allows for ValueTask to safely be used asynchronously with ICommand:

    • IAsyncValueCommand : ICommand
    • AsyncValueCommand : IAsyncValueCommand
    • IAsyncValueCommand<T> : ICommand
    • AsyncValueCommand<T> : IAsyncValueCommand<T>
    • IAsyncValueCommand<TExecute, TCanExecute> : IAsyncValueCommand<TExecute>
    • AsyncValueCommand<TExecute, TCanExecute> : IAsyncValueCommand<TExecute, TCanExecute>
  • Usage instructions

Setup

AsyncAwaitBestPractices

  • Available on NuGet: https://www.nuget.org/packages/AsyncAwaitBestPractices/
  • Add to any project supporting .NET Standard 1.0

AsyncAwaitBestPractices.MVVM

  • Available on NuGet: https://www.nuget.org/packages/AsyncAwaitBestPractices.MVVM/
  • Add to any project supporting .NET Standard 1.0

Why Do I Need This?

Online Courses

Join me in these DomeTrain courses where we'll learn everything you need to know to master asynchronous programming using async await in C# and .NET

<img width="659" height="440" alt="Asynchronous Programming in C#" src="https://github.com/user-attachments/assets/64cd8a04-fce9-46f0-8d40-25f994c7ec7a" />

<img width="640" height="435" alt="Parallel Programming in C#" src="https://github.com/user-attachments/assets/e6b579b3-d77f-40d1-86a1-3431901c22ee" />

Podcasts

No Dogma Podcast, Hosted by Bryan Hogan

Video

NDC London 2026

Correcting Common Async Await Mistakes in .NET 10

Explanation

Async/await is great but there are two subtle problems that can easily creep into code:

  1. Creating race conditions/concurrent execution (where you code things in the right order but the code executes in a different order than you expect)
  2. 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).

This library solves both of these problems.

To 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.

tl;dr A non-awaited Task doesn't rethrow exceptions and AsyncAwaitBestPractices.SafeFireAndForget ensures it will

Compiler-Generated Code for Async Method

Compiler-Generated Code for Async Method

(Source: Xamarin University: Using Async and Await)

The compiler transforms an async method into an IAsyncStateMachine class which allows the .NET Runtime to "remember" what the method has accomplished.

Move Next

(Source: Xamarin University: Using Async and Await)

The IAsyncStateMachine interface implements MoveNext(), a method the executes every time the await operator is used inside of the async method.

MoveNext() essentially runs your code until it reaches an await statement, then it returns 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.

Try/Catch in MoveNext()

Look closely at MoveNext(); notice that it is wrapped in a try/catch block.

Because 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!

How to Rethrow an Exception Caught By MoveNext

Now 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?

There are a few ways to rethrow exceptions that are thrown in an async method:

  1. Use the await keyword (Prefered)
    • e.g. await DoSomethingAsync()
  2. Use .GetAwaiter().GetResult()
    • e.g. DoSomethingAsync().GetAwaiter().GetResult()

The 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.

What About .Result or .Wait()?

Never, never, never, never, never use .Result or .Wait():

  1. 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.

  2. .Result or .Wait() rethrow your exception as a System.AggregateException, which makes it difficult to find the actual exception.

Usage

AsyncAwaitBestPractices

SafeFireAndForget

An extension method to safely fire-and-forget a Task.

SafeFireAndForget allows a Task to safely run on a different thread while the calling thread does not wait for its completion.

public static async void SafeFireAndForget(this System.Threading.Tasks.Task task, System.Action<System.Exception>? onException = null, bool continueOnCapturedContext = false)
public static async void SafeFireAndForget(this System.Threading.Tasks.ValueTask task, System.Action<System.Exception>? onException = null, bool continueOnCapturedContext = false)

On .NET 8.0 (and higher)

.NET 8.0 Introduces ConfigureAwaitOptions that allow users to customize the behavior when awaiting:

  • ConfigureAwaitOptions.None
    • No options specified
  • ConfigureAwaitOptions.SuppressThrowing
    • Avoids throwing an exception at the completion of awaiting a Task that ends in the Faulted or Canceled state
  • ConfigureAwaitOptions.ContinueOnCapturedContext
    • Attempts to marshal the continuation back to the original SynchronizationContext or TaskScheduler present on the originating thread at the time of the await
  • ConfigureAwaitOptions.ForceYielding
    • 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

For more information, check out Stephen Cleary's blog post, "ConfigureAwait in .NET 8".

public static void SafeFireAndForget(this System.Threading.Tasks.Task task, ConfigureAwaitOptions configureAwaitOptions, Action<Exception>? onException = null)

Basic Usage - Task

void HandleButtonTapped(object sender, EventArgs e)
{
    // Allows the async Task method to safely run on a different thread while the calling thread continues, not awaiting its completion
    // onException: If an Exception is thrown, print it to the Console
    ExampleAsyncMethod().SafeFireAndForget(onException: ex => Console.WriteLine(ex));

    // HandleButtonTapped continues execution here while `Ex
View on GitHub
GitHub Stars1.8k
CategoryDevelopment
Updated10h ago
Forks173

Languages

C#

Security Score

100/100

Audited on Mar 23, 2026

No findings