Correcting Common Async Await Mistakes in .NET

BrandonMinnickMBA 6,438 views 30 slides Aug 30, 2023
Slide 1
Slide 1 of 30
Slide 1
1
Slide 2
2
Slide 3
3
Slide 4
4
Slide 5
5
Slide 6
6
Slide 7
7
Slide 8
8
Slide 9
9
Slide 10
10
Slide 11
11
Slide 12
12
Slide 13
13
Slide 14
14
Slide 15
15
Slide 16
16
Slide 17
17
Slide 18
18
Slide 19
19
Slide 20
20
Slide 21
21
Slide 22
22
Slide 23
23
Slide 24
24
Slide 25
25
Slide 26
26
Slide 27
27
Slide 28
28
Slide 29
29
Slide 30
30

About This Presentation

Did you know that the .NET compiler turns our async methods into classes? And that .NET adds a try/catch block to each of these classes, potentially hiding thrown exceptions? It's true!

In this session, we will learn how to best use async/await in C# by analyzing how .NET compiles our async cod...


Slide Content

Correcting Common Async/Await Mistakes Brandon Minnick .NET Developer Advocate

ReadDataFromUrl async Task   ReadDataFromUrl ( string url ) { WebClient   wc  =  new   WebClient ();     byte[]  result =  await   wc.DownloadDataTaskAsync ( url );     string  data =  Encoding . ASCII .GetString (result);     LoadData (data); }

ReadDataFromUrl async Task   ReadDataFromUrl ( string url ) { WebClient   wc  =  new   WebClient ();     byte[]  result =  await   wc.DownloadDataTaskAsync ( url );     string  data =  Encoding . ASCII .GetString (result);     LoadData (data); } Thread 1

ReadDataFromUrl async Task   ReadDataFromUrl ( string url ) { WebClient   wc  =  new   WebClient ();     byte[]  result =  await   wc.DownloadDataTaskAsync ( url );     string  data =  Encoding . ASCII .GetString (result);     LoadData (data); } Thread 2* *Can be any thread other than Thread 1 e.g. Thread 32

ReadDataFromUrl async Task   ReadDataFromUrl ( string url ) { WebClient   wc  =  new   WebClient ();     byte[]  result =  await   wc.DownloadDataTaskAsync ( url );     string  data =  Encoding . ASCII .GetString (result);     LoadData (data); } Thread 1

Compiler Generated Code

async Task   ReadDataFromUrl ( string url ) { WebClient   wc  =  new   WebClient ();     byte[]  result =  await   wc.DownloadDataTaskAsync ( url );     string  data =  Encoding . ASCII .GetString (result);     LoadData (data); } ReadDataFromUrl

async Task   ReadDataFromUrl ( string url ) { WebClient   wc  =  new   WebClient ();     byte[]  result =  await   wc.DownloadDataTaskAsync ( url );     string  data =  Encoding . ASCII .GetString (result);     LoadData (data); } ReadDataFromUrl private sealed class < ReadDataFromUrl >d_1 : IAsyncStateMachine

async Task   ReadDataFromUrl ( string url ) { WebClient   wc  =  new   WebClient ();     byte[]  result =  await   wc.DownloadDataTaskAsync ( url );     string  data =  Encoding . ASCII .GetString (result);     LoadData (data); } ReadDataFromUrl private string <data>5_3; private byte[] <result>5_2; private WebClient < wc >5_1; public string url ;

async Task   ReadDataFromUrl ( string url ) { WebClient   wc  =  new   WebClient ();     byte[]  result =  await   wc.DownloadDataTaskAsync ( url );     string  data =  Encoding . ASCII .GetString (result);     LoadData (data); } ReadDataFromUrl private void MoveNext ();

ReadDataFromUrl_MoveNext public   void   MoveNext () { uint   num  = ( uint ) this .$PC;     this .$PC = -1;     try  {        switch  ( num ) {       case  0:            this .< wc >__0 =  new   WebClient ();              this .$awaiter0 =  this .< wc >__0.DownloadDataTaskAsync( this .url ). GetAwaiter ();             this .$PC = 1; ...              return ;              break ;          case  1:              this .< result >__1 =  this .$awaiter0.GetResult();              this .<data>__2 =  Encoding.ASCII.GetString ( this .< result >__1);              this .$ this.LoadData ( this .<data>__2);              break ;           default :              return ;     }    }     catch  ( Exception   exception ) { ... }     this .$PC = -1;     this .$ builder.SetResult (); }

public   void   MoveNext () { uint   num  = ( uint ) this .$PC;     this .$PC = -1;     try  {        switch  ( num ) {       case  0:            this .< wc >__0 =  new   WebClient ();              this .$awaiter0 =  this .< wc >__0.DownloadDataTaskAsync( this .url ). GetAwaiter ();             this .$PC = 1; ...              return ;              break ;          case  1:              this .< result >__1 =  this .$awaiter0.GetResult();              this .<data>__2 =  Encoding.ASCII.GetString ( this .< result >__1);              this .$ this.LoadData ( this .<data>__2);              break ;           default :              return ;     }    }     catch  ( Exception   exception ) { ... }     this .$PC = -1;     this .$ builder.SetResult (); } ReadDataFromUrl_MoveNext case  0: this .< wc >__0 =  new   WebClient (); this .$awaiter0 =  this .< wc >__0.DownloadDataTaskAsync( this .url ). GetAwaiter (); this .$PC = 1; ... return ;

public   void   MoveNext () { uint   num  = ( uint ) this .$PC;     this .$PC = -1;     try  {        switch  ( num ) {       case  0:            this .< wc >__0 =  new   WebClient ();              this .$awaiter0 =  this .< wc >__0.DownloadDataTaskAsync( this .url ). GetAwaiter ();             this .$PC = 1; ...              return ;              break ;          case  1:              this .< result >__1 =  this .$awaiter0.GetResult();              this .<data>__2 =  Encoding.ASCII.GetString ( this .< result >__1);              this .$ this.LoadData ( this .<data>__2);              break ;           default :              return ;     }    }     catch  ( Exception   exception ) { ... }     this .$PC = -1;     this .$ builder.SetResult (); } ReadDataFromUrl_MoveNext case  1: this .< result >__1 =  this .$awaiter0.GetResult(); this .<data>__2 =  Encoding.ASCII.GetString ( this .< result >__1); this .$ this.LoadData ( this .<data>__2); break ;

public   void   MoveNext () { uint   num  = ( uint ) this .$PC;     this .$PC = -1;     try  {        switch  ( num ) {       case  0:            this .< wc >__0 =  new   WebClient ();              this .$awaiter0 =  this .< wc >__0.DownloadDataTaskAsync( this .url ). GetAwaiter ();             this .$PC = 1; ...              return ;              break ;          case  1:              this .< result >__1 =  this .$awaiter0.GetResult();              this .<data>__2 =  Encoding.ASCII.GetString ( this .< result >__1);              this .$ this.LoadData ( this .<data>__2);              break ;           default :              return ;     }    }     catch  ( Exception   exception ) { ... }     this .$PC = -1;     this .$ builder.SetResult (); } ReadDataFromUrl_MoveNext try { catch ( Exception exception ) { . . . }

Quick Review

Async Keyword Adds 100 Bytes Every Async Method Becomes a Class

Await Every Task Non-awaited Tasks Hide Exceptions

Let’s Fix Some Code

Async/Await Best Practices

Async/Await Best Practices Never Use ` .Wait()` or ` .Result` Always use ` await` If synchronous, use ` . GetAwaiter (). GetResult ()`

Async/Await Best Practices Fire and Forget Tasks Use ` SafeFireAndForget ` NuGet: AsyncAwaitBestPractices

Async/Await Best Practices Avoid ` return await` Remove ` async` keyword Except: In ` try/catch` blocks Except: In ` using( … )` blocks

Async/Await Best Practices Utilize `. ConfigureAwait (false)` Except: When needing to return to calling thread Note: Only works when framework is using SynchronizationContext WPF WinForms Xamarin .NET MAUI WinUI ASP.NET Core

Async/Await Best Practices Utilize ` ValueTask ` When a method’s “hot path” doesn’t call ` await `

Async/Await Best Practices ` IAsyncEnumerable ` for Streaming Data Allows us to update UI as data arrives Better User Experience Use ` [ EnumeratorCancellation ] ` for CancellationToken

Async/Await Best Practices ` . WaitAsync ( CancellationToken )` Append to any async method missing a ` CancellationToken ` parameter

Async/Await Best Practices ` IAsyncDisposable ` The “await” executes at the end of the ` using ` block Can Append ConfigureAwait (false) CancellationToken not currently supported

https:// codetraveler.io / AsyncAwaitBestPractices / Resources

Thank You https:// codetraveler.io / AsyncAwaitBestPractices /

Section title