逐步解說:使用 Async 和 Await 存取 Web (C# 和 Visual Basic)

您可以更輕鬆的撰寫非同步程式且直覺地使用 [ Visual Studio 2012 ] 中加入的功能。 您可以撰寫看起來像同步程式碼的非同步程式碼,並讓編譯器處理困難的,非同步程式碼通常需要的回呼函式和接續。

(如需此功能的詳細資訊,請參閱使用 Async 和 Await 設計非同步程式 (C# 和 Visual Basic))。

這個逐步解說開頭為"將網站清單的位元組數目同步處理的 Windows Presentation Foundation (WPF) 應用程式"。 本逐步解說則將應用程式轉換為非同步方案藉由使用新的功能。

如果您不要建立應用程式,您可以下載「Async 範例:存取網路逐步解說 (C# 和 Visual Basic)」 開發人員程式碼範例

在這個逐步解說中,您將完成下列工作:

  • Create a WPF application.

  • Design a simple WPF MainWindow window.

  • Add a reference.

  • Add Imports statements or using directives.

  • Create a synchronous solution.

  • Test the synchronous solution.

  • Convert GetURLContents to an asynchronous method.

  • Convert SumPageSizes to an asynchronous method.

  • Convert startButton_Click to an asynchronous method.

  • Test the asynchronous solution.

  • Replace GetURLContentsAsync with a .NET Framework method.

  • Complete Code Examples from the Walkthrough

必要條件

在您的電腦上必須安裝Visual Studio 2012 。 如需詳細資訊,請參閱 Microsoft 網站 (英文)。

若要建立 WPF 應用程式

  1. 啟動 Visual Studio。

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

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

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

  4. 在 [名稱] 文字方塊中,輸入 AsyncExampleWPF,然後選取 [確定] 按鈕。

    新專案即會出現於 [方案總管] 中。

設計簡單的 WPF (MainWindow)。

  1. 在 Visual Studio 程式碼編輯器中,選取 [MainWindow.xaml] 索引標籤。

  2. 如果 [工具箱] 視窗不可見,開啟 [檢視] 功能表,然後選取 [工具箱]。

  3. 將 [按鈕][文字方塊] 控制項和控制項加入至 [(MainWindow)。] 視窗。

  4. 反白[文字方塊] 顯示控制項,並 [內容] ,在視窗中,將下列值:

    • 設定 [名稱] 屬性設定為 resultsTextBox。

    • 將 [高度] 屬性設為 250。

    • 將 [寬度] 屬性設為 500。

    • 在 [文字] 索引標籤上,指定一個等寬字型,例如 Lucida Console或Global Monospace。

  5. 反白[按鈕] 顯示控制項,並 [內容] ,在視窗中,設定下列值:

    • 將 [(Name)] 屬性設定為 [startButton]。

    • 從 [按鈕][內容] 變更屬性的值變更為 [開始]。

  6. 定位文字方塊和按鈕,讓這兩個出現在 [(MainWindow)。] 視窗。

    如需設計工具的詳細資訊,請參閱使用 XAML 設計工具建立 UI

若要加入參考

  1. 在 [方案總管],反白顯示您專案的名稱。

  2. 在功能表列上,選取 [專案], [新增參考]。

    [增益集管理員] 對話方塊隨即出現。

  3. 在對話方塊頂端,請確認您的專案以 .NET Framework 4.5為目標。

  4. 在 [組件] 區域,如果尚未選取,請選取 [架構] 。

  5. 在 [名稱] 清單中,選取 [System.Net.Http] 核取方塊。

  6. 選取 [確定] 按鈕來關閉對話方塊。

若要加入必要的 Imports 陳述式或 using 指示詞

  1. 在 [方案總管],開啟 MainWindow.xaml.vb 或 MainWindow.xaml.cs 的捷徑功能表,然後選取 [檢視程式碼]。

  2. 將下列 Imports 陳述式 (Visual Basic) 或 using 指示詞 (C#) 陳述式加入至程式碼檔案頂端 (如果尚未加入)。

    Imports System.Net.Http
    Imports System.Net
    Imports System.IO
    
    using System.Net.Http;
    using System.Net;
    using System.IO;
    

建立同步的應用程式

  1. 在 [設計] 視窗中, MainWindow.xaml,按兩下 [開始] 按鈕建立 MainWindow.xaml.vb 或 MainWindow.xaml.cs 中 startButton_Click 的事件處理常式。 一個代替方案,反白 [開始] 顯示按鈕,選取 [內容] 視窗的 [所選取項目的事件處理常式] 圖示,然後輸入在 [按一下] 文字方塊的 startButton_Click 。

  2. 在 MainWindow.xaml.vb 或 MainWindow.xaml.cs 中,將下列程式碼複製到startButton_Click的本體中。

    resultsTextBox.Clear()
    SumPageSizes()
    resultsTextBox.Text &= vbCrLf & "Control returned to startButton_Click."
    
    resultsTextBox.Clear();
    SumPageSizes();
    resultsTextBox.Text += "\r\nControl returned to startButton_Click.";
    

    程式碼呼叫驅動應用程式的方法, SumPageSizes,並且顯示訊息當控制項回傳至 startButton_Click時。

  3. 同步處理方案中的程式碼包含下列四個方法:

    • SumPageSizes,從 SetUpURLList 取得網頁 URL 清單然後呼叫 GetURLContents 和 DisplayResults 來處理每個 URL。

    • SetUpURLList製造並傳回一個網址的清單。

    • GetURLContents,下載每個網站內容並將內容做為位元組陣列。

    • DisplayResults,顯示每個 URL 的位元組陣列中的位元組數目將。

    複製下列四個方法,然後將它們貼到在 MainWindow.xaml.vb 或 MainWindow.xaml.cs 中 startButton_Click 的事件處理常式中。

    Private Sub SumPageSizes()
    
        ' Make a list of web addresses. 
        Dim urlList As List(Of String) = SetUpURLList()
    
        Dim total = 0
        For Each url In urlList
            ' GetURLContents returns the contents of url as a byte array. 
            Dim urlContents As Byte() = GetURLContents(url)
    
            DisplayResults(url, urlContents)
    
            ' Update the total.
            total += urlContents.Length
        Next 
    
        ' Display the total count for all of the web addresses.
        resultsTextBox.Text &= String.Format(vbCrLf & vbCrLf & "Total bytes returned:  {0}" & vbCrLf, total)
    End Sub 
    
    
    Private Function SetUpURLList() As List(Of String)
    
        Dim urls = New List(Of String) From
            {
                "https://msdn.microsoft.com/library/windows/apps/br211380.aspx",
                "https://msdn.microsoft.com",
                "https://msdn.microsoft.com/en-us/library/hh290136.aspx",
                "https://msdn.microsoft.com/en-us/library/ee256749.aspx",
                "https://msdn.microsoft.com/en-us/library/hh290138.aspx",
                "https://msdn.microsoft.com/en-us/library/hh290140.aspx",
                "https://msdn.microsoft.com/en-us/library/dd470362.aspx",
                "https://msdn.microsoft.com/en-us/library/aa578028.aspx",
                "https://msdn.microsoft.com/en-us/library/ms404677.aspx",
                "https://msdn.microsoft.com/en-us/library/ff730837.aspx"
            }
        Return urls
    End Function 
    
    
    Private Function GetURLContents(url As String) As Byte()
    
        ' The downloaded resource ends up in the variable named content. 
        Dim content = New MemoryStream()
    
        ' Initialize an HttpWebRequest for the current URL. 
        Dim webReq = CType(WebRequest.Create(url), HttpWebRequest)
    
        ' Send the request to the Internet resource and wait for 
        ' the response. 
        ' Note: you can't use HttpWebRequest.GetResponse in a Windows Store app. 
        Using response As WebResponse = webReq.GetResponse()
            ' Get the data stream that is associated with the specified URL. 
            Using responseStream As Stream = response.GetResponseStream()
                ' Read the bytes in responseStream and copy them to content.  
                responseStream.CopyTo(content)
            End Using 
        End Using 
    
        ' Return the result as a byte array. 
        Return content.ToArray()
    End Function 
    
    
    Private Sub DisplayResults(url As String, content As Byte())
    
        ' Display the length of each website. The string format  
        ' is designed to be used with a monospaced font, such as 
        ' Lucida Console or Global Monospace. 
        Dim bytes = content.Length
        ' Strip off the "http://". 
        Dim displayURL = url.Replace("http://", "")
        resultsTextBox.Text &= String.Format(vbCrLf & "{0,-58} {1,8}", displayURL, bytes)
    End Sub
    
    private void SumPageSizes()
    {
        // Make a list of web addresses.
        List<string> urlList = SetUpURLList(); 
    
        var total = 0;
        foreach (var url in urlList)
        {
            // GetURLContents returns the contents of url as a byte array. 
            byte[] urlContents = GetURLContents(url);
    
            DisplayResults(url, urlContents);
    
            // Update the total.
            total += urlContents.Length;
        }
    
        // Display the total count for all of the web addresses.
        resultsTextBox.Text += 
            string.Format("\r\n\r\nTotal bytes returned:  {0}\r\n", total);
    }
    
    
    private List<string> SetUpURLList()
    {
        var urls = new List<string> 
        { 
            "https://msdn.microsoft.com/library/windows/apps/br211380.aspx",
            "https://msdn.microsoft.com",
            "https://msdn.microsoft.com/en-us/library/hh290136.aspx",
            "https://msdn.microsoft.com/en-us/library/ee256749.aspx",
            "https://msdn.microsoft.com/en-us/library/hh290138.aspx",
            "https://msdn.microsoft.com/en-us/library/hh290140.aspx",
            "https://msdn.microsoft.com/en-us/library/dd470362.aspx",
            "https://msdn.microsoft.com/en-us/library/aa578028.aspx",
            "https://msdn.microsoft.com/en-us/library/ms404677.aspx",
            "https://msdn.microsoft.com/en-us/library/ff730837.aspx"
        };
        return urls;
    }
    
    
    private byte[] GetURLContents(string url)
    {
        // The downloaded resource ends up in the variable named content. 
        var content = new MemoryStream();
    
        // Initialize an HttpWebRequest for the current URL. 
        var webReq = (HttpWebRequest)WebRequest.Create(url);
    
        // Send the request to the Internet resource and wait for 
        // the response. 
        // Note: you can't use HttpWebRequest.GetResponse in a Windows Store app. 
        using (WebResponse response = webReq.GetResponse())
        {
            // Get the data stream that is associated with the specified URL. 
            using (Stream responseStream = response.GetResponseStream())
            {
                // Read the bytes in responseStream and copy them to content.  
                responseStream.CopyTo(content);
            }
        }
    
        // Return the result as a byte array. 
        return content.ToArray();
    }
    
    
    private void DisplayResults(string url, byte[] content)
    {
        // Display the length of each website. The string format  
        // is designed to be used with a monospaced font, such as 
        // Lucida Console or Global Monospace. 
        var bytes = content.Length;
        // Strip off the "http://".
        var displayURL = url.Replace("http://", "");
        resultsTextBox.Text += string.Format("\n{0,-58} {1,8}", displayURL, bytes);
    }
    

測試同步處理方案

  • 選取 F5 鍵執行程式,然後選取 [開始] 按鈕。

    類似下列清單的輸出應該會出現。

    msdn.microsoft.com/library/windows/apps/br211380.aspx        383832
    msdn.microsoft.com                                            33964
    msdn.microsoft.com/en-us/library/hh290136.aspx               225793
    msdn.microsoft.com/en-us/library/ee256749.aspx               143577
    msdn.microsoft.com/en-us/library/hh290138.aspx               237372
    msdn.microsoft.com/en-us/library/hh290140.aspx               128279
    msdn.microsoft.com/en-us/library/dd470362.aspx               157649
    msdn.microsoft.com/en-us/library/aa578028.aspx               204457
    msdn.microsoft.com/en-us/library/ms404677.aspx               176405
    msdn.microsoft.com/en-us/library/ff730837.aspx               143474
    
    Total bytes returned:  1834802
    
    Control returned to startButton_Click.
    

    請注意需要幾秒鐘顯示計數。 在這個時候,在等候被要求的資源下載時, UI 執行緒會被封鎖。 因此,在您選取 [開始] 按鈕之後,您就無法移動,最大化、最小化,甚至關閉視窗。 這些工作會失敗,直到位元組數開始出現。 如果網站沒有回應,您不會有網站失敗的指示。 停止等待和結束程式仍是困難。

    比較這個行為和非同步方案的 範例 。

轉換 GetURLContents 至非同步方法

  1. 若要轉換同步處理方案至非同步方案,最佳開始位置在 GetURLContents 中,因為呼叫 HttpWebRequest方法 GetResponse 以及 Stream 方法 CopyTo 是在應用程式存取該網站的位置。 .NET Framework 可讓轉換容易藉由提供兩個方法的非同步版本。

    如需 GetURLContents之方法的詳細資訊,請參閱 WebRequest

    注意事項注意事項

    在您依照本逐步解說中的步驟,數個編譯器錯誤隨即出現。您可以忽略它們並繼續進行這個逐步解說。

    變更在 GetURLContents 第三行呼叫的,從 GetResponse 至非同步的,以工作為基礎 GetResponseAsync 的方法。

    Using response As WebResponse = webReq.GetResponseAsync()
    
    using (WebResponse response = webReq.GetResponseAsync())
    
  2. GetResponseAsync傳回一個Task 。 在這種狀況下, 工作會傳回變數, TResult,具有型別 WebResponse。 這個工作承諾會產生實際 WebResponse 物件,在要求的資料下載完成後,而且工作已執行完成。

    若要從工作擷取 WebResponse 值,請將 等候 (Visual Basic) 或 等候 (C#) 運算子應用於對 GetResponseAsync的呼叫,如下列程式碼所示。

    Using response As WebResponse = Await webReq.GetResponseAsync()
    
    using (WebResponse response = await webReq.GetResponseAsync())
    

    等候運算子暫止目前的方法, GetURLContents,直到等候的工作已完成。 同時,控制項會返回目前方法的呼叫端。 在此範例中,目前的方法是GetURLContents,而且呼叫端是 SumPageSizes。 當工作完成時,就會讓必要的 WebResponse 物件被產生,作為在等待中工作的值並指派給變數 response。

    上述陳述可以分成下列兩個陳述來釐清已發生的事情。

    'Dim responseTask As Task(Of WebResponse) = webReq.GetResponseAsync() 
    'Using response As WebResponse = Await responseTask
    
    //Task<WebResponse> responseTask = webReq.GetResponseAsync(); 
    //using (WebResponse response = await responseTask)
    

    對webReq.GetResponseAsync 的呼叫會傳回 Task(Of WebResponse) 或 Task<WebResponse>。 然後等待運算子套用至工作來擷取 WebResponse 值。

    如果您的非同步方法有不需要依賴工作的完成就可以執行的工作,這方法可能會在這兩個陳述式之間繼續該工作,在呼叫非同步方法之後,在等待運算子被套用之前。 如需範例,請參閱如何:使用 Async 和 Await,同時發出多個 Web 要求 (C# 和 Visual Basic)如何:使用 Task.WhenAll 擴充非同步逐步解說的內容 (C# 和 Visual Basic)

  3. 由於您已加入Await 或 await 運算子在先前步驟中,會發生編譯器錯誤。 運算子只能被使用在標記 Async (Visual Basic) 或 (C#) async (C#) 修飾詞的方法中,。 當重複取代對 CopyTo 的呼叫用 CopyToAsync的呼叫之步驟時,會忽略錯誤。

    • 更改被 CopyToAsync呼叫的方法的名稱。

    • CopyTo 或 CopyToAsync 方法複製位元組至它的引數, content且不會傳回有意義的值。 在同步版本, 對CopyTo 是簡單陳述式且不傳回值。 非同步版本, CopyToAsync,傳回 Task。 工作函式像工作「(void)」及讓方法等候。 套用 Await 或 await 至對 CopyToAsync的呼叫,如下列程式碼中展示的。

      Await responseStream.CopyToAsync(content)
      
      await responseStream.CopyToAsync(content);
      

      上述陳述式省略下列兩行程式碼。

      ' CopyToAsync returns a Task, not a Task<T>. 
      'Dim copyTask As Task = responseStream.CopyToAsync(content) 
      
      ' When copyTask is completed, content contains a copy of 
      ' responseStream. 
      'Await copyTask
      
      // CopyToAsync returns a Task, not a Task<T>. 
      //Task copyTask = responseStream.CopyToAsync(content); 
      
      // When copyTask is completed, content contains a copy of 
      // responseStream. 
      //await copyTask;
      
  4. 在 GetURLContents 中仍要做的就是調整方法簽章。 您可以使用 Await或await運算子,只能在被標記為 Async(Visual Basic) 或 async (C#)修飾詞。 加入修飾詞來標記方法為 非同步方法,下列程式碼中會說明。

    Private Async Function GetURLContents(url As String) As Byte()
    
    private async byte[] GetURLContents(string url)
    
  5. 非同步方法的傳回型別只能為 TaskTask或 void 在 C#中。 在 Visual Basic 中,方法必須是一個 Task 或一個 Task(Of T)或一個Function ,或必須是 Sub。 通常,一個 Sub 方法 (Visual Basic) 或 (C#) void 的傳回型別只被使用在非同步事件處理常式,需要 Sub 或 void 。 在其他情況下,您使用 Task(T) ,如果完整方法具有 傳回傳回 陳述式會傳回型別 T 的值的,且您使用 Task ,如果完成方法不會傳回有意義的值。 您可以將 Task 傳回型別視為代表工作"Task(void)"。

    如需詳細資訊,請參閱非同步方法的傳回類型 (C# and Visual Basic)

    方法 GetURLContents 具有 return 陳述式,該陳述式傳回位元組陣列。 因此,非同步版本的傳回型別是工作 (Of T) ",其中 T 是位元組陣列。 在方法簽章中進行下列變更:

    • 將傳回型別變更為 Task(Of Byte()) (Visual Basic) 或 (C#)。 Task<byte[]>

    • 依照慣例,非同步方法以「Async 結束的名稱」命名 ,因此給方法重新命名為 GetURLContentsAsync。

    下列程式碼顯示這些變更。

    Private Async Function GetURLContentsAsync(url As String) As Task(Of Byte())
    
    private async Task<byte[]> GetURLContentsAsync(string url)
    

    有這些少數變更,將 GetURLContents 轉換為非同步方法則完成。

轉換 SumPageSizes 至非同步方法

  1. 為SumPageSizes重複前面程序中的步驟。 首先,請將 GetURLContents 的呼叫加入轉換為非同步呼叫。

    • 將被呼叫的方法的名稱從 GetURLContentsAsync改為 GetURLContents,如果您尚未這樣做。

    • 套用 Await 或 await 到 GetURLContentsAsync 傳回的工作來取得位元組陣列值。

    下列程式碼顯示這些變更。

    Dim urlContents As Byte() = Await GetURLContentsAsync(url)
    
    byte[] urlContents = await GetURLContentsAsync(url);
    

    這先前指派簡述下列兩行程式碼。

    ' GetURLContentsAsync returns a task. At completion, the task 
    ' produces a byte array. 
    'Dim getContentsTask As Task(Of Byte()) = GetURLContentsAsync(url) 
    'Dim urlContents As Byte() = Await getContentsTask
    
    // GetURLContentsAsync returns a Task<T>. At completion, the task 
    // produces a byte array. 
    //Task<byte[]> getContentsTask = GetURLContentsAsync(url); 
    //byte[] urlContents = await getContentsTask;
    
  2. 在方法簽章中進行下列變更:

    • 用 Async 或 async 修飾詞標誌方法。

    • 將「Async」加入方法名稱。

    • 因為 SumPageSizesAsync 不傳回 T 的值,這次沒有工作傳回變數。 (方法沒有 Return 或 return 陳述式)。不過,方法必須傳回一個 Task 讓其可以等待。 因此,請進行下列變更:

      • 在 Visual Basic 中,將方法類型變更從 Sub至 Function。 函式的傳回型別是 Task。

      • 在 C# 中,將方法的傳回型別變更從 void 為 Task。

    下列程式碼顯示這些變更。

    Private Async Function SumPageSizesAsync() As Task
    
    private async Task SumPageSizesAsync()
    

    將SumPageSizes 轉換為 SumPageSizesAsync 完成。

將 startButton_Click 轉換至非同步方法

  1. 在事件處理常式,將呼叫方法的名稱改變從 SumPageSizes 至 SumPageSizesAsync,如果您尚未這樣做的話)。

  2. 因為 SumPageSizesAsync 為非同步方法,改變事件處理常式中的程式碼來等待結果。

    對 SumPageSizesAsync 的呼叫反映在 GetURLContentsAsync中對的 CopyToAsync呼叫 。 此呼叫會傳回 Task,不是 Task(T)。

    在上一個程序中,您可以使用一個陳述式或兩個陳述式轉換這個呼叫。 下列程式碼顯示這些變更。

    '' One-step async call.
    Await SumPageSizesAsync()
    
    ' Two-step async call. 
    'Dim sumTask As Task = SumPageSizesAsync() 
    'Await sumTask
    
    // One-step async call.
    await SumPageSizesAsync();
    
    // Two-step async call. 
    //Task sumTask = SumPageSizesAsync(); 
    //await sumTask;
    
  3. 若要防止意外地重新輸入作業,請將下列陳述式在 startButton_Click 的 [啟動] 按鈕。

    ' Disable the button until the operation is complete.
    startButton.IsEnabled = False
    
    // Disable the button until the operation is complete.
    startButton.IsEnabled = false;
    

    您可以重新啟用按鈕在事件處理常式的結尾。

    ' Reenable the button in case you want to run the operation again.
    startButton.IsEnabled = True
    
    // Reenable the button in case you want to run the operation again.
    startButton.IsEnabled = true;
    

    如需重新進入的詳細資訊,請參閱 處理非同步應用程式中的重新進入 (C# 和 Visual Basic)

  4. 最後,加入 Async 或 async 修飾詞至宣告,讓事件處理常式可以等待 SumPagSizesAsync。

    Async Sub startButton_Click(sender As Object, e As RoutedEventArgs) Handles startButton.Click
    
    private async void startButton_Click(object sender, RoutedEventArgs e)
    

    通常,事件處理常式的名稱並未變更。 這是因為事件處理常式必須傳回 void 在 C# 中或Sub 程序在 Visual Basic 中,傳回型別並不會變更為 Task 。 因此,對 Task的傳回型別。

    轉換同步至非同步的專案處理完成。

測試非同步方案

  1. 選取 F5 鍵執行程式,然後選取 [開始] 按鈕。

  2. 類似同步處理方案的輸出應出現。 不過,請注意下列差異。

    • 在處理完成後,結果不一定會同時發生。 例如,兩個程式在 startButton_Click 中都包含一行,此行可以清除文字方塊。 目的是要清除執行之間的文字方塊中,如果您第二次選擇 [開始] 按鈕,在一組結果出現後。 在同步版本,在計算第二次出現之前,文字方塊被清除,當下載完成時,且 UI 執行緒可以自由執行其他工作。 在非同步版本,文字方塊在您選取 [開始] 按鈕後立即清除。

    • 最重要的是,在下載期間, UI 執行緒不會被阻擋。 當 Web 資源下載,計數,並且顯示時,您可以移動或調整視窗大小。 如果其中一個網站速度太慢或沒有回應,您可以選取 [關閉] 按鈕則會取消作業 (x 右上角的紅色欄位)。

使用 .NET Framework 方法取代方法 GetURLContentsAsync

  1. .NET Framework 4.5 提供您可以使用的許多非同步方法。 其中一個, HttpClient 方法 GetByteArrayAsync(String),執行您在此逐步解說需要的步驟。 您可以使用它而不是您在舊版的程序中所建立的 GetURLContentsAsync 方法。

    第一步就是建立在 SumPageSizesAsync中的 HttpClient 物件。 在此方法的開頭加入下列宣告。

    ' Declare an HttpClient object and increase the buffer size. The 
    ' default buffer size is 65,536. 
    Dim client As HttpClient =
        New HttpClient() With {.MaxResponseContentBufferSize = 1000000}
    
    // Declare an HttpClient object and increase the buffer size. The 
    // default buffer size is 65,536.
    HttpClient client =
        new HttpClient() { MaxResponseContentBufferSize = 1000000 };
    
  2. 在 SumPageSizesAsync, 將對您的 GetURLContentsAsync 方法的呼叫取代為 HttpClient 方法的呼叫。

    Dim urlContents As Byte() = Await client.GetByteArrayAsync(url)
    
    byte[] urlContents = await client.GetByteArrayAsync(url);               
    
  3. 移除或註解所撰寫的 GetURLContentsAsync 方法。

  4. 選取 F5 鍵執行程式,然後選取 [開始] 按鈕。

    這個專案版本的行為應該與「測試非同步方案」程序中所描述的行為符合,但需要您更少的投入時間。

範例

下列程式碼包含轉換的完整範例從同步處理至非同步方案藉由使用您所撰寫的非同步 GetURLContentsAsync 方法。 請注意它強烈類似原始,同步處理方案。

' Add the following Imports statements, and add a reference for System.Net.Http. 
Imports System.Net.Http
Imports System.Net
Imports System.IO

Class MainWindow

    Async Sub startButton_Click(sender As Object, e As RoutedEventArgs) Handles startButton.Click

        ' Disable the button until the operation is complete.
        startButton.IsEnabled = False

        resultsTextBox.Clear()

        '' One-step async call.
        Await SumPageSizesAsync()

        ' Two-step async call. 
        'Dim sumTask As Task = SumPageSizesAsync() 
        'Await sumTask

        resultsTextBox.Text &= vbCrLf & "Control returned to button1_Click." 

        ' Reenable the button in case you want to run the operation again.
        startButton.IsEnabled = True 
    End Sub 


    Private Async Function SumPageSizesAsync() As Task

        ' Make a list of web addresses. 
        Dim urlList As List(Of String) = SetUpURLList()

        Dim total = 0
        For Each url In urlList
            Dim urlContents As Byte() = Await GetURLContentsAsync(url)

            ' The previous line abbreviates the following two assignment statements. 

            ' GetURLContentsAsync returns a task. At completion, the task 
            ' produces a byte array. 
            'Dim getContentsTask As Task(Of Byte()) = GetURLContentsAsync(url) 
            'Dim urlContents As Byte() = Await getContentsTask

            DisplayResults(url, urlContents)

            ' Update the total.
            total += urlContents.Length
        Next 

        ' Display the total count for all of the websites.
        resultsTextBox.Text &= String.Format(vbCrLf & vbCrLf &
                                             "Total bytes returned:  {0}" & vbCrLf, total)
    End Function 


    Private Function SetUpURLList() As List(Of String)

        Dim urls = New List(Of String) From
            {
                "https://msdn.microsoft.com/library/windows/apps/br211380.aspx",
                "https://msdn.microsoft.com",
                "https://msdn.microsoft.com/en-us/library/hh290136.aspx",
                "https://msdn.microsoft.com/en-us/library/ee256749.aspx",
                "https://msdn.microsoft.com/en-us/library/hh290138.aspx",
                "https://msdn.microsoft.com/en-us/library/hh290140.aspx",
                "https://msdn.microsoft.com/en-us/library/dd470362.aspx",
                "https://msdn.microsoft.com/en-us/library/aa578028.aspx",
                "https://msdn.microsoft.com/en-us/library/ms404677.aspx",
                "https://msdn.microsoft.com/en-us/library/ff730837.aspx"
            }
        Return urls
    End Function 


    Private Async Function GetURLContentsAsync(url As String) As Task(Of Byte())

        ' The downloaded resource ends up in the variable named content. 
        Dim content = New MemoryStream()

        ' Initialize an HttpWebRequest for the current URL. 
        Dim webReq = CType(WebRequest.Create(url), HttpWebRequest)

        ' Send the request to the Internet resource and wait for 
        ' the response. 
        Using response As WebResponse = Await webReq.GetResponseAsync()

            ' The previous statement abbreviates the following two statements. 

            'Dim responseTask As Task(Of WebResponse) = webReq.GetResponseAsync() 
            'Using response As WebResponse = Await responseTask 

            ' Get the data stream that is associated with the specified URL. 
            Using responseStream As Stream = response.GetResponseStream()
                ' Read the bytes in responseStream and copy them to content.  
                Await responseStream.CopyToAsync(content)

                ' The previous statement abbreviates the following two statements. 

                ' CopyToAsync returns a Task, not a Task<T>. 
                'Dim copyTask As Task = responseStream.CopyToAsync(content) 

                ' When copyTask is completed, content contains a copy of 
                ' responseStream. 
                'Await copyTask 
            End Using 
        End Using 

        ' Return the result as a byte array. 
        Return content.ToArray()
    End Function 


    Private Sub DisplayResults(url As String, content As Byte())

        ' Display the length of each website. The string format  
        ' is designed to be used with a monospaced font, such as 
        ' Lucida Console or Global Monospace. 
        Dim bytes = content.Length
        ' Strip off the "http://". 
        Dim displayURL = url.Replace("http://", "")
        resultsTextBox.Text &= String.Format(vbCrLf & "{0,-58} {1,8}", displayURL, bytes)
    End Sub 

End Class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

// Add the following using directives, and add a reference for System.Net.Http. 
using System.Net.Http;
using System.IO;
using System.Net;

namespace AsyncExampleWPF
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private async void startButton_Click(object sender, RoutedEventArgs e)
        {
            // Disable the button until the operation is complete.
            startButton.IsEnabled = false;

            resultsTextBox.Clear();

            // One-step async call.
            await SumPageSizesAsync();

            // Two-step async call. 
            //Task sumTask = SumPageSizesAsync(); 
            //await sumTask;

            resultsTextBox.Text += "\r\nControl returned to startButton_Click.\r\n";

            // Reenable the button in case you want to run the operation again.
            startButton.IsEnabled = true;
        }


        private async Task SumPageSizesAsync()
        {
            // Make a list of web addresses.
            List<string> urlList = SetUpURLList();

            var total = 0;

            foreach (var url in urlList)
            {
                byte[] urlContents = await GetURLContentsAsync(url);

                // The previous line abbreviates the following two assignment statements. 

                // GetURLContentsAsync returns a Task<T>. At completion, the task 
                // produces a byte array. 
                //Task<byte[]> getContentsTask = GetURLContentsAsync(url); 
                //byte[] urlContents = await getContentsTask;

                DisplayResults(url, urlContents);

                // Update the total.          
                total += urlContents.Length;
            }
            // Display the total count for all of the websites.
            resultsTextBox.Text +=
                string.Format("\r\n\r\nTotal bytes returned:  {0}\r\n", total);
        }


        private List<string> SetUpURLList()
        {
            List<string> urls = new List<string> 
            { 
                "https://msdn.microsoft.com/library/windows/apps/br211380.aspx",
                "https://msdn.microsoft.com",
                "https://msdn.microsoft.com/en-us/library/hh290136.aspx",
                "https://msdn.microsoft.com/en-us/library/ee256749.aspx",
                "https://msdn.microsoft.com/en-us/library/hh290138.aspx",
                "https://msdn.microsoft.com/en-us/library/hh290140.aspx",
                "https://msdn.microsoft.com/en-us/library/dd470362.aspx",
                "https://msdn.microsoft.com/en-us/library/aa578028.aspx",
                "https://msdn.microsoft.com/en-us/library/ms404677.aspx",
                "https://msdn.microsoft.com/en-us/library/ff730837.aspx"
            };
            return urls;
        }


        private async Task<byte[]> GetURLContentsAsync(string url)
        {
            // The downloaded resource ends up in the variable named content. 
            var content = new MemoryStream();

            // Initialize an HttpWebRequest for the current URL. 
            var webReq = (HttpWebRequest)WebRequest.Create(url);

            // Send the request to the Internet resource and wait for 
            // the response.                 
            using (WebResponse response = await webReq.GetResponseAsync())

            // The previous statement abbreviates the following two statements. 

            //Task<WebResponse> responseTask = webReq.GetResponseAsync(); 
            //using (WebResponse response = await responseTask)
            {
                // Get the data stream that is associated with the specified url. 
                using (Stream responseStream = response.GetResponseStream())
                {
                    // Read the bytes in responseStream and copy them to content. 
                    await responseStream.CopyToAsync(content);

                    // The previous statement abbreviates the following two statements. 

                    // CopyToAsync returns a Task, not a Task<T>. 
                    //Task copyTask = responseStream.CopyToAsync(content); 

                    // When copyTask is completed, content contains a copy of 
                    // responseStream. 
                    //await copyTask;
                }
            }
            // Return the result as a byte array. 
            return content.ToArray();
        }


        private void DisplayResults(string url, byte[] content)
        {
            // Display the length of each website. The string format  
            // is designed to be used with a monospaced font, such as 
            // Lucida Console or Global Monospace. 
            var bytes = content.Length;
            // Strip off the "http://".
            var displayURL = url.Replace("http://", "");
            resultsTextBox.Text += string.Format("\n{0,-58} {1,8}", displayURL, bytes);
        }
    }
}

下列程式碼包含使用 HttpClient 方法, GetByteArrayAsync的完整範例。

' Add the following Imports statements, and add a reference for System.Net.Http. 
Imports System.Net.Http
Imports System.Net
Imports System.IO

Class MainWindow

    Async Sub startButton_Click(sender As Object, e As RoutedEventArgs) Handles startButton.Click

        resultsTextBox.Clear()

        ' Disable the button until the operation is complete.
        startButton.IsEnabled = False 

        ' One-step async call.
        Await SumPageSizesAsync()

        '' Two-step async call. 
        'Dim sumTask As Task = SumPageSizesAsync() 
        'Await sumTask

        resultsTextBox.Text &= vbCrLf & "Control returned to button1_Click." 

        ' Reenable the button in case you want to run the operation again.
        startButton.IsEnabled = True 
    End Sub 


    Private Async Function SumPageSizesAsync() As Task

        ' Declare an HttpClient object and increase the buffer size. The 
        ' default buffer size is 65,536. 
        Dim client As HttpClient =
            New HttpClient() With {.MaxResponseContentBufferSize = 1000000}

        ' Make a list of web addresses. 
        Dim urlList As List(Of String) = SetUpURLList()

        Dim total = 0
        For Each url In urlList
            ' GetByteArrayAsync returns a task. At completion, the task 
            ' produces a byte array. 
            Dim urlContents As Byte() = Await client.GetByteArrayAsync(url)

            ' The following two lines can replace the previous assignment statement. 
            'Dim getContentsTask As Task(Of Byte()) = client.GetByteArrayAsync(url) 
            'Dim urlContents As Byte() = Await getContentsTask

            DisplayResults(url, urlContents)

            ' Update the total.
            total += urlContents.Length
        Next 

        ' Display the total count for all of the websites.
        resultsTextBox.Text &= String.Format(vbCrLf & vbCrLf &
                                             "Total bytes returned:  {0}" & vbCrLf, total)
    End Function 


    Private Function SetUpURLList() As List(Of String)

        Dim urls = New List(Of String) From
            {
                "https://msdn.microsoft.com/library/windows/apps/br211380.aspx",
                "https://msdn.microsoft.com",
                "https://msdn.microsoft.com/en-us/library/hh290136.aspx",
                "https://msdn.microsoft.com/en-us/library/ee256749.aspx",
                "https://msdn.microsoft.com/en-us/library/hh290138.aspx",
                "https://msdn.microsoft.com/en-us/library/hh290140.aspx",
                "https://msdn.microsoft.com/en-us/library/dd470362.aspx",
                "https://msdn.microsoft.com/en-us/library/aa578028.aspx",
                "https://msdn.microsoft.com/en-us/library/ms404677.aspx",
                "https://msdn.microsoft.com/en-us/library/ff730837.aspx"
            }
        Return urls
    End Function 


    Private Sub DisplayResults(url As String, content As Byte())

        ' Display the length of each website. The string format  
        ' is designed to be used with a monospaced font, such as 
        ' Lucida Console or Global Monospace. 
        Dim bytes = content.Length
        ' Strip off the "http://". 
        Dim displayURL = url.Replace("http://", "")
        resultsTextBox.Text &= String.Format(vbCrLf & "{0,-58} {1,8}", displayURL, bytes)
    End Sub 

End Class
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;

// Add the following using directives, and add a reference for System.Net.Http. 
using System.Net.Http;
using System.IO;
using System.Net;


namespace AsyncExampleWPF
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
        }

        private async void startButton_Click(object sender, RoutedEventArgs e)
        {
            resultsTextBox.Clear();

            // Disable the button until the operation is complete.
            startButton.IsEnabled = false;

            // One-step async call.
            await SumPageSizesAsync();

            //// Two-step async call. 
            //Task sumTask = SumPageSizesAsync(); 
            //await sumTask;

            resultsTextBox.Text += "\r\nControl returned to startButton_Click.\r\n";

            // Reenable the button in case you want to run the operation again.
            startButton.IsEnabled = true;
        }


        private async Task SumPageSizesAsync()
        {
            // Declare an HttpClient object and increase the buffer size. The 
            // default buffer size is 65,536.
            HttpClient client =
                new HttpClient() { MaxResponseContentBufferSize = 1000000 };

            // Make a list of web addresses.
            List<string> urlList = SetUpURLList();

            var total = 0;

            foreach (var url in urlList)
            {
                // GetByteArrayAsync returns a task. At completion, the task 
                // produces a byte array. 
                byte[] urlContents = await client.GetByteArrayAsync(url);               

                // The following two lines can replace the previous assignment statement. 
                //Task<byte[]> getContentsTask = client.GetByteArrayAsync(url); 
                //byte[] urlContents = await getContentsTask;

                DisplayResults(url, urlContents);

                // Update the total.
                total += urlContents.Length;
            }

            // Display the total count for all of the websites.
            resultsTextBox.Text +=
                string.Format("\r\n\r\nTotal bytes returned:  {0}\r\n", total);
        }


        private List<string> SetUpURLList()
        {
            List<string> urls = new List<string> 
            { 
                "https://msdn.microsoft.com/library/windows/apps/br211380.aspx",
                "https://msdn.microsoft.com",
                "https://msdn.microsoft.com/en-us/library/hh290136.aspx",
                "https://msdn.microsoft.com/en-us/library/ee256749.aspx",
                "https://msdn.microsoft.com/en-us/library/hh290138.aspx",
                "https://msdn.microsoft.com/en-us/library/hh290140.aspx",
                "https://msdn.microsoft.com/en-us/library/dd470362.aspx",
                "https://msdn.microsoft.com/en-us/library/aa578028.aspx",
                "https://msdn.microsoft.com/en-us/library/ms404677.aspx",
                "https://msdn.microsoft.com/en-us/library/ff730837.aspx"
            };
            return urls;
        }


        private void DisplayResults(string url, byte[] content)
        {
            // Display the length of each website. The string format  
            // is designed to be used with a monospaced font, such as 
            // Lucida Console or Global Monospace. 
            var bytes = content.Length;
            // Strip off the "http://".
            var displayURL = url.Replace("http://", "");
            resultsTextBox.Text += string.Format("\n{0,-58} {1,8}", displayURL, bytes);
        }
    }
}

請參閱

工作

如何:使用 Task.WhenAll 擴充非同步逐步解說的內容 (C# 和 Visual Basic)

如何:使用 Async 和 Await,同時發出多個 Web 要求 (C# 和 Visual Basic)

逐步解說:搭配非同步方法使用偵錯工具

參考

async (C# 參考)

await (C# 參考)

Await 運算子 (Visual Basic)

Async (Visual Basic)

概念

使用 Async 和 Await 設計非同步程式 (C# 和 Visual Basic)

非同步方法的傳回類型 (C# and Visual Basic)

使用非同步方式存取檔案 (C# 和 Visual Basic)

其他資源

Async 範例: 存取 Web 逐步解說 (C# and Visual Basic)

Task-based 非同步程式設計 (TAP)

快速開始: 使用等待運算子為非同步程式設計