VB.NET Advanced Topics: Asynchronous Programming Examples

Asynchronous programming in VB.NET allows applications to remain responsive while performing long-running operations. This is crucial for user interface applications and services that need to handle multiple requests concurrently. This section provides practical examples of using Async and Await keywords.

Note: Asynchronous programming relies on the .NET Framework 4.5 or later. Ensure your project targets a compatible framework version.

1. Basic Asynchronous Operation

This example demonstrates a simple asynchronous method that simulates a time-consuming task, like downloading data from a web service. The UI thread remains responsive while the operation completes.

Example: `DownloadDataAsync` Method

' Simulate a time-consuming operation
Public Async Function DownloadDataAsync(url As String) As Task(Of String)
    Console.WriteLine("Starting download...")
    ' Simulate network delay
    Await Task.Delay(3000)
    Console.WriteLine("Download complete.")
    Return $"Data from {url}"
End Function

' Calling the asynchronous method from a button click event handler
Private Async Sub ButtonDownload_Click(sender As Object, e As EventArgs) Handles ButtonDownload.Click
    Me.Cursor = Cursors.WaitCursor
    ButtonDownload.Enabled = False

    Try
        Dim result As String = Await DownloadDataAsync("http://example.com/data")
        MessageBox.Show($"Received: {result}")
    Catch ex As Exception
        MessageBox.Show($"An error occurred: {ex.Message}")
    Finally
        Me.Cursor = Cursors.Default
        ButtonDownload.Enabled = True
    End Try
End Sub

2. Handling Multiple Asynchronous Operations Concurrently

When you need to perform several asynchronous operations at the same time, Task.WhenAll is your best friend. It allows you to launch multiple tasks and wait for all of them to complete.

Example: Fetching data from multiple sources

Private Async Function FetchMultipleSourcesAsync() As Task
    Dim task1 As Task(Of String) = DownloadDataAsync("http://source1.com/data")
    Dim task2 As Task(Of String) = DownloadDataAsync("http://source2.com/data")
    Dim task3 As Task(Of String) = DownloadDataAsync("http://source3.com/data")

    ' Wait for all tasks to complete
    Dim results As String() = Await Task.WhenAll(task1, task2, task3)

    Console.WriteLine("All downloads completed.")
    For Each result As String In results
        Console.WriteLine($"- {result}")
    Next
End Function

' Calling the method
Private Async Sub ButtonFetchAll_Click(sender As Object, e As EventArgs) Handles ButtonFetchAll.Click
    Me.Cursor = Cursors.WaitCursor
    ButtonFetchAll.Enabled = False

    Try
        Await FetchMultipleSourcesAsync()
    Catch ex As Exception
        MessageBox.Show($"An error occurred: {ex.Message}")
    Finally
        Me.Cursor = Cursors.Default
        ButtonFetchAll.Enabled = True
    End Try
End Sub

3. Cancelling Asynchronous Operations

Long-running operations should often be cancellable. CancellationTokenSource and CancellationToken are used to signal cancellation requests to asynchronous tasks.

Example: Download with cancellation support

Private Async Function DownloadWithCancellationAsync(url As String, cancellationToken As CancellationToken) As Task(Of String)
    Console.WriteLine($"Starting download for {url}...")
    For i As Integer = 0 To 10
        ' Check if cancellation has been requested
        cancellationToken.ThrowIfCancellationRequested()

        Console.WriteLine($"Downloading chunk {i + 1}/10...")
        Await Task.Delay(300, cancellationToken) ' Pass token to delay
    Next
    Console.WriteLine($"Download for {url} complete.")
    Return $"Data from {url} (fully downloaded)"
End Function

' In your form class
Private cancellationTokenSource As CancellationTokenSource = Nothing

Private Async Sub ButtonStartDownload_Click(sender As Object, e As EventArgs) Handles ButtonStartDownload.Click
    If cancellationTokenSource IsNot Nothing AndAlso Not cancellationTokenSource.IsCancellationRequested Then
        MessageBox.Show("A download is already in progress.")
        Return
    End If

    cancellationTokenSource = New CancellationTokenSource()
    Dim token As CancellationToken = cancellationTokenSource.Token

    ButtonStartDownload.Enabled = False
    ButtonCancelDownload.Enabled = True
    Me.Cursor = Cursors.WaitCursor

    Try
        Dim result As String = Await DownloadWithCancellationAsync("http://largefile.com/download", token)
        MessageBox.Show($"Download Result: {result}")
    Catch ex As OperationCanceledException
        MessageBox.Show("Download was cancelled.")
    Catch ex As Exception
        MessageBox.Show($"An error occurred: {ex.Message}")
    Finally
        ButtonStartDownload.Enabled = True
        ButtonCancelDownload.Enabled = False
        Me.Cursor = Cursors.Default
        cancellationTokenSource.Dispose()
        cancellationTokenSource = Nothing
    End Try
End Sub

Private Sub ButtonCancelDownload_Click(sender As Object, e As EventArgs) Handles ButtonCancelDownload.Click
    If cancellationTokenSource IsNot Nothing AndAlso Not cancellationTokenSource.IsCancellationRequested Then
        cancellationTokenSource.Cancel()
        MessageBox.Show("Cancellation requested.")
    End If
End Sub

4. Using `ConfigureAwait(False)`

In library code or scenarios where you don't need to resume execution on the original synchronization context (like a UI thread), using ConfigureAwait(False) can improve performance and avoid potential deadlocks.

Example: Library method using ConfigureAwait(False)

' This method could be part of a class library
Public Async Function GetDataFromExternalServiceAsync(url As String) As Task(Of String)
    ' Use HttpClient for web requests (using statement ensures disposal)
    Using client As New HttpClient()
        ' Await the GetStringAsync operation
        ' ConfigureAwait(False) is important here to avoid capturing the UI context
        Dim response As String = Await client.GetStringAsync(url).ConfigureAwait(False)
        Return response
    End Using
End Function

' Example of calling it from a UI thread
Private Async Sub ButtonGetData_Click(sender As Object, e As EventArgs) Handles ButtonGetData.Click
    Try
        Dim data As String = Await GetDataFromExternalServiceAsync("http://api.service.com/data")
        TextBoxResult.Text = data
    Catch ex As Exception
        MessageBox.Show($"Error: {ex.Message}")
    End Try
End Sub

When ConfigureAwait(False) is used, the code following the Await will not necessarily resume on the original context. This is beneficial in libraries to prevent blocking the calling thread and to avoid deadlocks, especially when the caller might also be on a UI thread.

Back to Top