VB.NET Async Await – Step by Step with Example (2026)

With Async and Await you prevent your VB.NET application from freezing during long-running operations. In this post you will see step by step how to switch from Thread.Sleep to Task.Delay, what Fire & Forget means, and when you need to create your own Tasks, with GIF demos and complete example code.

Need help?

VB.NET development for your project?

I work with VB.NET and C# every day. Whether async architectures or complete applications, just get in touch.

Code – VB.NET Await

In the following section I will explain the details of how the code works.

Without async

We start with the synchronous approach, as you can see in the animated image below. I click the button, which does its work and executes the 3 statements one after another. Unfortunately the „Sleep“ call from the Thread class runs on the GUI thread, which causes the application to stutter. The classic alternative would be a BackgroundWorker, but Async/Await is far more elegant.

This happens after the handler has executed the first „WriteLine“ statement and then literally goes to „sleep“. I don’t know about you, but nobody can talk to me while I’m sleeping, and it’s the same here!

VB.NET Form freezes during Sleep
VB.NET Form freezes during Sleep

For the next step, open the button’s click event handler and look at the following code:

Private Sub btnAwait_Click(sender As Object, e As EventArgs) Handles btnAwait.Click
    Debug.WriteLine("Before")
    Thread.Sleep(3000)
    Debug.WriteLine("After")
End Sub

To really understand and „feel“ the upcoming effect, click the button once. Then try to move the form, and you will notice that nothing responds. Even though I’m simply trying to drag the form by its border, nothing happens at all.

This is because we are literally putting the main thread, i.e. the GUI thread (the graphical interface), to sleep.

Frustration builds up – User Experience (UX)

The interface freezes, the user clicks again and in the worst case triggers multiple database calls. Anyone who has stared at a frozen application for minutes knows how frustrating that is.

Going async – with the VB.NET Await statement

Now we convert the example using the modern approach, i.e. „VB.NET Await“ or „VB.NET Async Await“, available since Visual Studio 2012. In this case we can achieve it with 2 very simple changes! First, we mark the method as asynchronous using the Async modifier.

Then we replace the „Thread Sleep“ wait function with its equivalent from the Task class called Delay.

Private Async Sub btnAwait_Click(sender As Object, e As EventArgs) Handles btnAwait.Click
    Debug.WriteLine("Before")
    Await Task.Delay(3000)
    Debug.WriteLine("After")
End Sub

The last addition is the Await operator, which, simply put, waits for the task returned by Delay to complete.

No more freezing!

VB.NET Form no longer freezes during Sleep
VB.NET Form no longer freezes during Sleep

No anti-aging products needed here, just kidding. Nothing freezes anymore. Try it right away and click the button again after making the changes. The button’s hover effect will remain visible and with a sufficient delay interval you will also be able to move the form.

Async Await is not always that simple

Now we have reached an important point: unfortunately we cannot always proceed this easily.

Async Await is not always that simple
Async Await is not always that simple

Suitable prerequisites

To use Async/Await we need suitable prerequisites, or we have to create them ourselves. You cannot simply mark any arbitrary method as Async and hope it always works. To wait for a task to complete using the Await operator, we need an actual task to wait for.

Some .NET Framework functions return a task as their return value. This includes the „Task.Delay“ function shown above. Further examples are HttpClient.GetStringAsync, File.ReadAllTextAsync (see also: VB.NET Read Text File) or SqlCommand.ExecuteReaderAsync, all of which return a Task that you can await.

Creating your own prerequisites

When we don’t find such convenient prerequisites, i.e. no ready-made task to await, we have to build our own. But even that is not really difficult with the modern .NET Framework, provided you know how! We will leave this topic aside for now and focus on a few more basics first.

Linear flow – The usual business

Before we deal with creating our own tasks, let’s look at a small foundational concept. Beginners often confuse different situations, so I want to briefly explain 2 basics here. Normally the code we write is executed line by line, i.e. linearly.

Imagine you have 3 statements defined in a method called „DoStatements“:

Private Sub DoStatements()
    Statement1()
    Statement2()
    Statement3()
End Sub
Linear code, i.e. individual statements in a method (Sub)
Linear code, i.e. individual statements in a method (Sub)

When you call the „DoStatements“ method, the statements inside (1, 2, 3) are executed one after another. To make the contrast even more obvious, call the „DoStatements“ method 3 times. Calling „DoStatements“ is of course itself a statement, just sayin‘.

Triple linear call of the DoStatements method
Triple linear call of the DoStatements method

Each individual call to the parent method (DoStatements) in turn calls the child statements 1-3.

The execution order would be:

  • DoStatements
    • Statement 1
    • Statement 2
    • Statement 3
  • DoStatements
    • Statement 1

..and so on.

Need help?

VB.NET development for your project?

I work with VB.NET and C# every day. Whether async architectures or complete applications, just get in touch.

Fire and Forget

Now, regarding the „Async Await“ pattern, we make a small change to the code above and see how it behaves. Mark the „DoStatements“ method as Async by appending the corresponding keyword: Instead of a „private method“, it is now a „private, asynchronous method“:

Private Async Sub DoStatements()
    Statement1()
    Statement2()
    Statement3()
End Sub

Unfortunately not much happens at first, except that we get a new error message. We are warned that we declare „Hey, this method should run asynchronously“, but we don’t actually perform any Await operations inside it. Basically it tells us: „Why should this method be async if you’re not doing any async stuff in there!?“.

Async DoStatements method error - missing Await operators
Async DoStatements method error – missing Await operators

To demonstrate „Fire & Forget“ properly, we either follow the IDE’s suggestion or use a small trick.

Workaround for the demo

Simply call the following in the first line of the „DoStatements“ method:

Await Task.CompletedTask

Like this (or somewhere else in the Sub):

Private Async Sub DoStatements()
    Await Task.CompletedTask
    Statement1()
    Statement2()
    Statement3()
End Sub

This way we await a completed task to trick the „Async Sub“ into thinking asynchronous work is happening. Please note that this code is for demonstration purposes only! The error message above is now gone and the Sub runs asynchronously.

Now it runs a(sync)hronously

If you now call the „DoStatements“ method 3 times, it will not execute the same way as before.

Take a look at this diagram:

Before synchronous – after asynchronous
Before synchronous – after asynchronous

Previously steps 4 and 5, i.e. subsequent calls to „DoStatements“, waited for the previous calls to finish. This is now different because steps 1-3 are fired off one after another. As a result they run more or less simultaneously in the background.

Use cases for Fire & Forget

For a practical use case of our „Fire & Forget“ approach from above, think of a chat server. I myself have used something similar for a kind of order server at a former employer. Sure, you could avoid the GUI thread entirely and work in a console application instead.

However that is not always possible and naturally depends on the client’s requirements. A rudimentary version could look something like this (you could of course add Enum values for different states like „Starting, Stopping, Running“ etc.):

Public Class ChatServer

    Public Property IsRunning As Boolean

    Public Async Sub Start()
        CheckAndThrowIfAlreadyRunning()
        IsRunning = True
        RaiseEvent Started(Me, EventArgs.Empty)
        While IsRunning
            Await DoServerWork()
        End While
        RaiseEvent Stopped(Me, EventArgs.Empty)
    End Sub

    Private Function DoServerWork() As Task
        ' simulate some work..
        Return Task.Delay(200)
    End Function

    Public Sub [Stop]()
        CheckAndThrowIfNotRunning()
    End Sub

    Private Sub CheckAndThrowIfAlreadyRunning()
        If IsRunning Then
            Throw New InvalidOperationException("The ChatServer is already running")
        End If
    End Sub

    Private Sub CheckAndThrowIfNotRunning()
        If Not IsRunning Then
            Throw New InvalidOperationException("The ChatServer isn't running")
        End If
    End Sub

    Public Event Started As EventHandler

    Public Event Stopped As EventHandler

End Class

If you then create an email server or additional similar servers, they obviously need to run simultaneously. Such a server could, for example, write logs asynchronously to a file, see VB.NET Write Text File for how that works. Let’s start these rudimentary server examples in a Forms application:

Public Class Form1

    Private _chatServer As ChatServer

    ' Private _emailServer As EmailServer

    ' Private _orderServer As OrderServer

    Sub New()
        InitializeComponent()
        _chatServer = New ChatServer()
        ' _emailServer = New EmailServer()
        ' _orderServer = New OrderServer()
    End Sub

    Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
        StartAllServers()
    End Sub

    Private Sub StartAllServers()
        ' fires off all async, not waiting for a single "start" to "finish"
        _chatServer.Start()
        ' _emailServer.Start()
        ' _orderServer.Start()
    End Sub

End Class

As noted in the comment, the „StartAllServers“ method fires off all start calls one after another. It does not wait for any of them to complete, fire & forget. You could of course subscribe to the individual events and add more.

This way you could create a „decoupled“ communication between the GUI and possibly also the individual „server modules“.

Need help?

VB.NET development for your project?

I work with VB.NET and C# every day. Whether async architectures or complete applications, just get in touch.

FAQ

What does Await do in VB.NET?

Await waits for an asynchronous task to complete without blocking the GUI thread. The application remains responsive during the wait.

What is the difference between Thread.Sleep and Await Task.Delay?

Thread.Sleep blocks the current thread completely and the GUI freezes. Await Task.Delay releases the thread and resumes the method after the time has elapsed, without blocking the interface.

What does Fire and Forget mean in VB.NET?

Fire and Forget means calling an Async method without waiting for it to finish. The method continues to run in the background while the calling code proceeds immediately.

Can I mark any method as Async in VB.NET?

No. An Async method must contain at least one Await operator. Without Await the method runs synchronously despite the Async modifier and the compiler issues a warning.

Downloads

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert

Robert Skibbe
Datenschutz-Übersicht

Diese Website verwendet Cookies, damit wir dir die bestmögliche Benutzererfahrung bieten können. Cookie-Informationen werden in deinem Browser gespeichert und führen Funktionen aus, wie das Wiedererkennen von dir, wenn du auf unsere Website zurückkehrst, und hilft unserem Team zu verstehen, welche Abschnitte der Website für dich am interessantesten und nützlichsten sind.