共用方式為


異步程式中的控制流程 (Visual Basic)

您可以使用 AsyncAwait 關鍵詞,更輕鬆地撰寫和維護異步程式。 不過,如果您不瞭解程序的運作方式,結果可能會讓您大吃一驚。 本主題會透過一個簡單的非同步程式來追蹤控制流,以便展示每當控制從一個方法轉移到另一個方法時,哪個資訊被傳遞。

備註

AsyncAwait 關鍵詞是在 Visual Studio 2012 中引進的。

一般而言,您可以使用 Async 修飾詞標記包含異步程式代碼的方法。 在以異步修飾詞標示的方法中,您可以使用 Await (Visual Basic) 運算符來指定方法暫停以等候呼叫的異步進程完成的位置。 如需詳細資訊,請參閱使用 Async 和 Await 進行異步程式設計(Visual Basic)。

下列範例會使用異步方法,將指定網站的內容下載為字串,並顯示字串的長度。 此範例包含下列兩種方法。

  • startButton_Click,其會呼叫 AccessTheWebAsync 並顯示結果。

  • AccessTheWebAsync,它會將網站的內容下載為字串,並傳回字串的長度。 AccessTheWebAsync 使用異步 HttpClient 方法 GetStringAsync(String),下載內容。

編號顯示行會出現在整個程式中的戰略點,以協助您瞭解程序執行的方式,並說明標記的每個點會發生什麼事。 顯示行會標示為 「ONE」 到 「SIX」。標籤代表程式到達這些程式代碼行的順序。

下列程式代碼顯示程式的大綱。

Class MainWindow

    Private Async Sub StartButton_Click(sender As Object, e As RoutedEventArgs) Handles StartButton.Click

        ' ONE
        Dim getLengthTask As Task(Of Integer) = AccessTheWebAsync()

        ' FOUR
        Dim contentLength As Integer = Await getLengthTask

        ' SIX
        ResultsTextBox.Text &=
            vbCrLf & $"Length of the downloaded string: {contentLength}." & vbCrLf

    End Sub

    Async Function AccessTheWebAsync() As Task(Of Integer)

        ' TWO
        Dim client As HttpClient = New HttpClient()
        Dim getStringTask As Task(Of String) =
            client.GetStringAsync("https://learn.microsoft.com")

        ' THREE
        Dim urlContents As String = Await getStringTask

        ' FIVE
        Return urlContents.Length
    End Function

End Class

每個已標記的位置 「ONE」 到 「SIX」 會顯示程式目前狀態的相關信息。 會產生下列輸出:

ONE:   Entering startButton_Click.
           Calling AccessTheWebAsync.

TWO:   Entering AccessTheWebAsync.
           Calling HttpClient.GetStringAsync.

THREE: Back in AccessTheWebAsync.
           Task getStringTask is started.
           About to await getStringTask & return a Task<int> to startButton_Click.

FOUR:  Back in startButton_Click.
           Task getLengthTask is started.
           About to await getLengthTask -- no caller to return to.

FIVE:  Back in AccessTheWebAsync.
           Task getStringTask is complete.
           Processing the return statement.
           Exiting from AccessTheWebAsync.

SIX:   Back in startButton_Click.
           Task getLengthTask is finished.
           Result from AccessTheWebAsync is stored in contentLength.
           About to display contentLength and exit.

Length of the downloaded string: 33946.

設定程式

您可以從 MSDN 下載本主題所使用的程式代碼,也可以自行建置。

備註

若要執行此範例,您必須在計算機上安裝Visual Studio 2012或更新版本,以及已安裝 .NET Framework 4.5 或更新版本。

下載程式

您可以從 異步程式中的異步範例:控制流程下載本主題的應用程式。 下列步驟會開啟並執行程式。

  1. 解壓縮下載的檔案,然後啟動Visual Studio。

  2. 在功能表欄上,選擇 [ 檔案]、[ 開啟]、[ 專案/方案]。

  3. 流覽至保存解壓縮範例程式代碼的資料夾,開啟方案 (.sln) 檔案,然後選擇 F5 索引鍵來建置並執行專案。

自行建置程式

下列 Windows Presentation Foundation (WPF) 專案包含本主題的程式碼範例。

若要執行專案,請執行下列步驟:

  1. 啟動 Visual Studio。

  2. 在功能表欄上,選擇 [ 檔案]、[ 新增]、[ 專案]。

    [ 新增專案 ] 對話框隨即開啟。

  3. 在 [ 已安裝的範本 ] 窗格中,選擇 [Visual Basic],然後從專案類型清單中選擇 [WPF 應用程式 ]。

  4. 輸入 AsyncTracer 作為專案的名稱,然後選擇 [ 確定] 按鈕。

    新的專案會出現在 [方案總管] 中。

  5. 在 Visual Studio Code 編輯器中,選擇 MainWindow.xaml 索引標籤

    如果看不到索引標籤,請在 [方案總管] 中開啟 MainWindow.xaml 的快捷方式選單,然後選擇 [檢視程式碼]。

  6. 在MainWindow.xaml的 XAML 檢視中,將程式代碼取代為下列程序代碼。

    <Window
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        xmlns:d="http://schemas.microsoft.com/expression/blend/2008" xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006" mc:Ignorable="d" x:Class="MainWindow"
        Title="Control Flow Trace" Height="350" Width="525">
        <Grid>
            <Button x:Name="StartButton" Content="Start" HorizontalAlignment="Left" Margin="221,10,0,0" VerticalAlignment="Top" Width="75"/>
            <TextBox x:Name="ResultsTextBox" HorizontalAlignment="Left" TextWrapping="Wrap" VerticalAlignment="Bottom" Width="510" Height="265" FontFamily="Lucida Console" FontSize="10" VerticalScrollBarVisibility="Visible" d:LayoutOverrides="HorizontalMargin"/>
    
        </Grid>
    </Window>
    

    包含文字框和按鈕的簡單視窗會出現在MainWindow.xaml 的設計 檢視中。

  7. 加入 System.Net.Http 的參考。

  8. [方案總管] 中,開啟MainWindow.xaml.vb的快捷方式功能表,然後選擇 [ 檢視程序代碼]。

  9. 在 MainWindow.xaml.vb 中,將程式代碼取代為下列程序代碼。

    ' Add an Imports statement and a reference for System.Net.Http.
    Imports System.Net.Http
    
    Class MainWindow
    
        Private Async Sub StartButton_Click(sender As Object, e As RoutedEventArgs) Handles StartButton.Click
    
            ' The display lines in the example lead you through the control shifts.
            ResultsTextBox.Text &= "ONE:   Entering StartButton_Click." & vbCrLf &
                "           Calling AccessTheWebAsync." & vbCrLf
    
            Dim getLengthTask As Task(Of Integer) = AccessTheWebAsync()
    
            ResultsTextBox.Text &= vbCrLf & "FOUR:  Back in StartButton_Click." & vbCrLf &
                "           Task getLengthTask is started." & vbCrLf &
                "           About to await getLengthTask -- no caller to return to." & vbCrLf
    
            Dim contentLength As Integer = Await getLengthTask
    
            ResultsTextBox.Text &= vbCrLf & "SIX:   Back in StartButton_Click." & vbCrLf &
                "           Task getLengthTask is finished." & vbCrLf &
                "           Result from AccessTheWebAsync is stored in contentLength." & vbCrLf &
                "           About to display contentLength and exit." & vbCrLf
    
            ResultsTextBox.Text &=
                String.Format(vbCrLf & "Length of the downloaded string: {0}." & vbCrLf, contentLength)
        End Sub
    
        Async Function AccessTheWebAsync() As Task(Of Integer)
    
            ResultsTextBox.Text &= vbCrLf & "TWO:   Entering AccessTheWebAsync."
    
            ' Declare an HttpClient object.
            Dim client As HttpClient = New HttpClient()
    
            ResultsTextBox.Text &= vbCrLf & "           Calling HttpClient.GetStringAsync." & vbCrLf
    
            ' GetStringAsync returns a Task(Of String).
            Dim getStringTask As Task(Of String) = client.GetStringAsync("https://learn.microsoft.com")
    
            ResultsTextBox.Text &= vbCrLf & "THREE: Back in AccessTheWebAsync." & vbCrLf &
                "           Task getStringTask is started."
    
            ' AccessTheWebAsync can continue to work until getStringTask is awaited.
    
            ResultsTextBox.Text &=
                vbCrLf & "           About to await getStringTask & return a Task(Of Integer) to StartButton_Click." & vbCrLf
    
            ' Retrieve the website contents when task is complete.
            Dim urlContents As String = Await getStringTask
    
            ResultsTextBox.Text &= vbCrLf & "FIVE:  Back in AccessTheWebAsync." &
                vbCrLf & "           Task getStringTask is complete." &
                vbCrLf & "           Processing the return statement." &
                vbCrLf & "           Exiting from AccessTheWebAsync." & vbCrLf
    
            Return urlContents.Length
        End Function
    
    End Class
    
  10. 選擇 F5 鍵以執行程式,然後選擇 [ 開始 ] 按鈕。

    應該會出現下列輸出:

    ONE:   Entering startButton_Click.
               Calling AccessTheWebAsync.
    
    TWO:   Entering AccessTheWebAsync.
               Calling HttpClient.GetStringAsync.
    
    THREE: Back in AccessTheWebAsync.
               Task getStringTask is started.
               About to await getStringTask & return a Task<int> to startButton_Click.
    
    FOUR:  Back in startButton_Click.
               Task getLengthTask is started.
               About to await getLengthTask -- no caller to return to.
    
    FIVE:  Back in AccessTheWebAsync.
               Task getStringTask is complete.
               Processing the return statement.
               Exiting from AccessTheWebAsync.
    
    SIX:   Back in startButton_Click.
               Task getLengthTask is finished.
               Result from AccessTheWebAsync is stored in contentLength.
               About to display contentLength and exit.
    
    Length of the downloaded string: 33946.
    

追蹤程式

步驟一和二

前兩個顯示行會將路徑追蹤為 startButton_Click 呼叫 AccessTheWebAsync,並 AccessTheWebAsync 呼叫異步 HttpClient 方法 GetStringAsync(String)。 下圖概述方法之間的呼叫順序。

步驟一和二

AccessTheWebAsyncclient.GetStringAsync 的回傳型別皆為 Task<TResult>。 針對 AccessTheWebAsync,TResult 是整數。 針對 GetStringAsync,TResult 是字串。 如需異步方法傳回型別的詳細資訊,請參閱 異步傳回型別 (Visual Basic)

當控制權轉移回呼叫端時,返回任務的異步方法會傳回任務實例。 當呼叫的方法中遇到運算符 Await 或當呼叫的方法結束時,控制會從異步方法返回至調用者。 標示為「THREE」到「SIX」的顯示行會追蹤這個過程的這一部分。

步驟三

在 中 AccessTheWebAsync,會呼叫異步方法 GetStringAsync(String) 以下載目標網頁的內容。 當 client.GetStringAsync 傳回時,控制權會從 AccessTheWebAsync 返回至 client.GetStringAsync

方法 client.GetStringAsync 傳回一個字串的工作,該工作被指派給 getStringTask 中的 AccessTheWebAsync 變數。 範例程式中的下一行會顯示 對 client.GetStringAsync 和 指派的呼叫。

Dim getStringTask As Task(Of String) = client.GetStringAsync("https://learn.microsoft.com")

您可以將這項任務視為由 client.GetStringAsync 承諾最終產生實際字串。 同時,如果 AccessTheWebAsync 有不需要依賴client.GetStringAsync提供的承諾字串的工作,那麼 AccessTheWebAsync 可以在 等待的同時繼續進行這些工作。 在此範例中,標示為 「THREE」 的下列幾行輸出,代表執行獨立工作的機會

THREE: Back in AccessTheWebAsync.
           Task getStringTask is started.
           About to await getStringTask & return a Task<int> to startButton_Click.

下列語句會在等候 AccessTheWebAsync 時暫停 getStringTask 的進度。

Dim urlContents As String = Await getStringTask

下圖顯示從 client.GetStringAsync 到指派給 getStringTask 的控制流程,以及從建立 getStringTask 到應用 Await 運算子的過程。

步驟三

await 表達式會 AccessTheWebAsync 暫停,直到 client.GetStringAsync 傳回為止。 目前,控制權會傳回給呼叫 AccessTheWebAsyncstartButton_Click 的一方。

備註

一般而言,您會立即等候對異步方法的呼叫。 例如,下列指派可以取代先前建立的程式碼,然後等候 getStringTaskDim urlContents As String = Await client.GetStringAsync("https://learn.microsoft.com")

本主題稍後會套用 await 運算子,以容納透過程式標記控制流程的輸出行。

步驟四

宣告的傳回型別 AccessTheWebAsyncTask(Of Integer)。 因此,當 AccessTheWebAsync 被暫停時,它會將整數任務傳回給 startButton_Click。 您應該瞭解傳回的工作不是 getStringTask。 傳回的工作是整數的新工作,代表在暫停的方法中尚未完成的任務。AccessTheWebAsync 這項工作是AccessTheWebAsync的承諾,將在工作完成時產生一個整數。

下列語句會將此工作指派給 getLengthTask 變數。

Dim getLengthTask As Task(Of Integer) = AccessTheWebAsync()

如同 AccessTheWebAsync 中,startButton_Click 在等候異步工作(getLengthTask)之前,可以繼續進行不依賴於該工作的其他工作。 下列輸出行代表該工作:

FOUR:  Back in startButton_Click.
           Task getLengthTask is started.
           About to await getLengthTask -- no caller to return to.

當在等待startButton_Click時,getLengthTask的進度會暫停。 下列分派語句會暫停 startButton_Click,直到 AccessTheWebAsync 完成為止。

Dim contentLength As Integer = Await getLengthTask

在下圖中,箭號會顯示從 await 運算式 AccessTheWebAsync 到 值 getLengthTask指派的控制流程,後面接著在 中 startButton_Click 正常處理,直到 getLengthTask 等候為止。

步驟四

步驟五

client.GetStringAsync 發出已完成的訊號時,AccessTheWebAsync 中的處理會從暫停中釋放,並可以繼續執行超過 await 語句。 下列幾行輸出代表繼續處理:

FIVE:  Back in AccessTheWebAsync.
           Task getStringTask is complete.
           Processing the return statement.
           Exiting from AccessTheWebAsync.

return 語句urlContents.Length的運算元會儲存在由AccessTheWebAsync傳回的任務中。 await 表達式會從 getLengthTaskstartButton_Click擷取該值。

下圖顯示在client.GetStringAsync(和getStringTask)完成之後的控制權傳輸。

步驟 5

AccessTheWebAsync 會執行至完成,控制權會傳回給 startButton_Click,而 startButton_Click 正在等候完成。

步驟六

AccessTheWebAsync 發出已完成的訊號時,處理可以繼續超過 startButton_Async 中的 await 語句。 事實上,程式沒有什麼可做的事。

下列幾行輸出代表startButton_Async中的恢復處理:

SIX:   Back in startButton_Click.
           Task getLengthTask is finished.
           Result from AccessTheWebAsync is stored in contentLength.
           About to display contentLength and exit.

await 表達式會從 getLengthTaskAccessTheWebAsync傳回語句的作數整數值擷取。 下列語句會將該值指派給 contentLength 變數。

Dim contentLength As Integer = Await getLengthTask

下圖顯示從 AccessTheWebAsync 傳回控制權到 startButton_Click

步驟六

另請參閱