Giter Site home page Giter Site logo

michaelstgt / asyncawaitbestpractices Goto Github PK

View Code? Open in Web Editor NEW

This project forked from brminnick/asyncawaitbestpractices

0.0 2.0 0.0 225 KB

Extensions for System.Threading.Tasks.Task

Home Page: https://www.nuget.org/packages/AsyncAwaitBestPractices.MVVM/

License: MIT License

C# 100.00%

asyncawaitbestpractices's Introduction

AsyncAwaitBestPractices

Build Status

Extensions for System.Threading.Tasks.Task.

Inspired by John Thiriet's blog posts: Removing Async Void and MVVM - Going Async With AsyncCommand.

AsyncAwaitBestPractices

NuGet

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

  • SafeFireAndForget
    • An extension method to safely fire-and-forget a Task:
  • WeakEventManager
    • Avoids memory leaks when events are not unsubscribed
    • Used by AsyncCommand and AsyncCommand<T>
  • Usage instructions

AsyncAwaitBestPractices.MVVM

NuGet

Setup

AsyncAwaitBestPractices

AsyncAwaitBestPractices.MVVM

Why Do I Need This?

Video

Update Conference 2018, Correcting Common Mistakes in Asynchronous .NET Code: https://www.youtube.com/watch?v=yyT6dSjq-nE

Explaination

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.

And by the way, tl;dr A non-awaited Task doesn't rethrow exceptions so use this library!

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:

public static async void SafeFireAndForget(this System.Threading.Tasks.Task task, bool continueOnCapturedContext = true, System.Action<System.Exception> onException = null)
void HandleButtonTapped(object sender, EventArgs e)
{
    // Allows the async Task method to safely run on a different thread while not awaiting its completion
    // If an exception is thrown, Console.WriteLine
    ExampleAsyncMethod().SafeFireAndForget(onException: ex => Console.WriteLine(ex.ToString()));

    // HandleButtonTapped continues execution here while `ExampleAsyncMethod()` is running on a different thread
    // ...
}

async Task ExampleAsyncMethod()
{
    await Task.Delay(1000);
}

WeakEventManager

An event implementation that enables the garbage collector to collect an object without needing to unsubscribe event handlers, inspired by Xamarin.Forms.WeakEventManager:

readonly WeakEventManager _weakEventManager = new WeakEventManager();

public event EventHandler CanExecuteChanged
{
    add => _weakEventManager.AddEventHandler(value);
    remove => _weakEventManager.RemoveEventHandler(value);
}

public void RaiseCanExecuteChanged() => _weakEventManager.HandleEvent(this, EventArgs.Empty, nameof(CanExecuteChanged));

WeakEventManager<T>

readonly WeakEventManager<string> _errorOcurredEventManager = new WeakEventManager<string>();

public event EventHandler<string> ErrorOcurred
{
    add => _errorOcurredEventManager.AddEventHandler(value);
    remove => _errorOcurredEventManager.RemoveEventHandler(value);
}

public void RaiseErrorOcurred(string message) => _weakEventManager.HandleEvent(this, message, nameof(ErrorOcurred));

AsyncAwaitBestPractices.MVVM

AsyncCommand

Allows for Task to safely be used asynchronously with ICommand:

  • AsyncCommand<T> : IAsyncCommand<T>
  • IAsyncCommand<T> : ICommand
  • AsyncCommand : IAsyncCommand
  • IAsyncCommand : ICommand
public AsyncCommand(Func<T, Task> execute,
                     Func<object, bool> canExecute = null,
                     Action<Exception> onException = null,
                     bool continueOnCapturedContext = true)   
public AsyncCommand(Func<Task> execute,
                     Func<object, bool> canExecute = null,
                     Action<Exception> onException = null,
                     bool continueOnCapturedContext = true)
public class ExampleClass
{
    public ExampleClass()
    {
        ExampleAsyncCommand = new AsyncCommand(ExampleAsyncMethod);
        ExampleAsyncIntCommand = new AsyncCommand<int>(ExampleAsyncMethodWithIntParameter);
        ExampleAsyncExceptionCommand = new AsyncCommand(ExampleAsyncMethodWithException, onException: ex => Console.WriteLine(ex.ToString()));
        ExampleAsyncCommandNotReturningToTheCallingThread = new AsyncCommand(ExampleAsyncMethod, continueOnCapturedContext: false);
    }

    public IAsyncCommand ExampleAsyncCommand { get; }
    public IAsyncCommand<int> ExampleAsyncIntCommand { get; }
    public IAsyncCommand ExampleAsyncExceptionCommand { get; }
    public IAsyncCommand ExampleAsyncCommandNotReturningToTheCallingThread { get; }

    async Task ExampleAsyncMethod()
    {
        await Task.Delay(1000);
    }
  
    async Task ExampleAsyncMethodWithIntParameter(int parameter)
    {
        await Task.Delay(parameter);
    }

    async Task ExampleAsyncMethodWithException()
    {
        await Task.Delay(1000);
        throw new Exception();
    }

    void ExecuteCommands()
    {
        ExampleAsyncCommand.Execute(null);
        ExampleAsyncIntCommand.Execute(1000);
        ExampleAsyncExceptionCommand.Execute(null);
        ExampleAsyncCommandNotReturningToTheCallingThread.Execute(null);
    }
}

asyncawaitbestpractices's People

Contributors

andrewchungxam avatar billwagner avatar brminnick avatar pasisavolainen avatar

Watchers

 avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.