Async and Await
Programs spend a lot of time waiting — for a web request, a
database, a file. Async and Await let code wait without blocking a
thread, so a desktop app stays responsive and a server can handle more
requests.
Module Program
Async Function FetchPriceAsync(product As String) As Task(Of Integer)
Await Task.Delay(100) ' simulate a slow network call
Return product.Length * 10
End Function
Async Function RunAsync() As Task
Console.WriteLine("fetching...")
Dim tea = Await FetchPriceAsync("tea")
Dim coffee = Await FetchPriceAsync("coffee")
Console.WriteLine($"total: {tea + coffee}")
End Function
Sub Main()
RunAsync().GetAwaiter().GetResult()
End Sub
End Module
fetching...
total: 90
- An
Async Functionreturns aTask(no result) orTask(Of T)(a result of typeT). By convention its name ends inAsync. Awaitpauses the method until the task finishes, without blocking the thread, then continues with the result.- VB doesn't allow
Async Sub Main, so a console app starts the async code fromMainand waits for it with.GetAwaiter().GetResult().
Running tasks concurrently
The two awaits above run one after another. Start independent tasks
first, then await them together with Task.WhenAll:
Module Program
Async Function WorkAsync(name As String, ms As Integer) As Task(Of String)
Await Task.Delay(ms)
Return $"{name} done"
End Function
Async Function RunAsync() As Task
Dim sw = Stopwatch.StartNew()
Dim tasks = {WorkAsync("A", 300), WorkAsync("B", 300), WorkAsync("C", 300)}
Dim results = Await Task.WhenAll(tasks)
Console.WriteLine(String.Join(", ", results))
Console.WriteLine($"took under 600 ms: {sw.ElapsedMilliseconds < 600}")
End Function
Sub Main()
RunAsync().GetAwaiter().GetResult()
End Sub
End Module
A done, B done, C done
took under 600 ms: True
Three 300 ms operations finished in about 300 ms in total.
Task.WhenAny returns as soon as the first one finishes.
A real HTTP request
' illustrative: needs network access
Imports System.Net.Http
Module Program
Private ReadOnly client As New HttpClient()
Async Function PageSizeAsync(url As String) As Task(Of Integer)
Dim html = Await client.GetStringAsync(url)
Return html.Length
End Function
Sub Main()
Dim size = PageSizeAsync("https://learn.microsoft.com").GetAwaiter().GetResult()
Console.WriteLine($"{size} characters")
End Sub
End Module
Create one HttpClient and reuse it; creating one per request can use
up network sockets under load.
Async in Windows Forms
Event handlers are the one place where Async Sub is right:
' illustrative: inside a Windows Forms form
Private Async Sub LoadButton_Click(sender As Object, e As EventArgs) Handles LoadButton.Click
LoadButton.Enabled = False
StatusLabel.Text = "Loading..."
Dim text = Await File.ReadAllTextAsync("big-report.txt")
ResultBox.Text = text
StatusLabel.Text = "Done"
LoadButton.Enabled = True
End Sub
While the file loads, the window keeps responding. After Await, the
code continues on the UI thread, so it can update controls safely.
Common mistakes
Async Subanywhere except event handlers — callers can't await it, and its exceptions can crash the program. UseAsync Function ... As Task.- Calling
.Resultor.Wait()on a task from UI code — it can deadlock. Await all the way up. - Forgetting
Await: the method keeps going before the work is done, and exceptions go unobserved.