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.
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.
' 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
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.
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
Long-running operations should often be cancellable. CancellationTokenSource
and CancellationToken
are used to signal cancellation requests to asynchronous tasks.
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
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.
' 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.