Asynchronous Programming

27.1 Why Async?

I/O-bound work (HTTP, DB, file) waits without blocking threads, so UIs stay responsive and servers scale.

CPU-bound work: offload with Task.Run (in UI apps) or parallelize (Module 29).

27.2 Core Syntax

Async Function... As Task / Task(Of T) Event handlers only

Asynchronously wait. The continuation resumes on the captured context (UI thread).

Task.WhenAll / WhenAny Task.Delay, Task.Run, Task.FromResult, Task.CompletedTask Don't capture the context (libraries)

CancellationToken / CancellationTokenSource IProgress(Of T) / Progress(Of T) Report progress to the UI TaskCompletionSource(Of T) Wrap callback/event APIs as tasks ValueTask(Of T) Allocation-free fast paths (consume)

Public Async Function GetOrderSummaryAsync(id As Integer, ct As Threading.CancellationToken) As Task(Of OrderSummary)
    Dim orderTask = _orderRepo.GetAsync(id, ct)
    Dim customerTask = _customerApi.GetForOrderAsync(id, ct)
    Await Task.WhenAll(orderTask, customerTask)                        ' run concurrently
    Return New OrderSummary(orderTask.Result, customerTask.Result)    ' safe: tasks already completed
End Function

27.3 Wrapping Event-Based APIs

Public Function WaitForExitAsync(p As Process) As Task
    Dim tcs As New TaskCompletionSource(Of Boolean)(TaskCreationOptions.RunContinuationsAsynchronously)
    p.EnableRaisingEvents = True
    AddHandler p.Exited, Sub(s, e) tcs.TrySetResult(True)
    If p.HasExited Then tcs.TrySetResult(True)
    Return tcs.Task
End Function

27.4 Async Pitfalls

❌ .Result / .Wait() on UI or legacy ASP.NET threads → deadlock. Go async all the way.

❌ Async Sub outside event handlers → unobserved exceptions crash the process.

❌ Forgetting Await (fire-and-forget) → lost exceptions. If it's intentional, handle errors explicitly.

❌ Capturing the UI context in libraries → use ConfigureAwait(False).

✅ Pass CancellationToken everywhere. Use timeouts (cts.CancelAfter). Don't block in Sub Main except at the bridge (GetAwaiter().GetResult()).

VB limitations: no Await inside Catch/Finally blocks (as of VB 16.9: you can't await in Catch/Finally), no Await For Each, no Await Using. Restructure code (set a flag in Catch, then await after the Try block).