在 C# 或 Visual Basic 中呼叫非同步 API

Windows 包含許多非同步 API,確保應用程式在運作時仍能保持回應,而這些工作可能需要較長時間。 本主題討論如何在 C# 或 Microsoft Visual Basic 中使用非同步方法。

非同步 API 讓你的應用程式不必等大型操作完成後才繼續執行。 例如,一個從網路下載資訊的應用程式可能會花好幾秒鐘等待資訊到達。 如果你用同步方法來取得資訊,應用程式會被封鎖,直到方法回傳。 應用程式不會回應使用者互動,且因為看起來不回應,使用者可能會感到挫折。 透過提供非同步 API,Windows 有助於確保應用程式在執行長時間操作時仍能對使用者保持回應。

Windows 中的大多數非同步 API 沒有同步對應的,所以你需要確保了解如何在 Windows 應用程式中使用 C# 或 Visual Basic 的非同步 API。 這裡我們示範如何呼叫非同步 API。

使用非同步 API

依慣例,非同步方法的名稱以「Async」結尾。 你通常會根據使用者的操作來呼叫非同步 API,例如當使用者點擊按鈕時。 在事件處理程序中呼叫非同步方法,是使用非同步 API 最簡單的方法之一。 這裡我們以 等待 運算子作為範例。

假設你有一個應用程式,列出某個地區的部落格文章標題。 應用程式有一個 按鈕 ,使用者點擊它可以取得標題。 標題會顯示在 文字區塊中。 當使用者點擊按鈕時,應用程式在等待部落格網站資訊時保持反應良好非常重要。 為了確保這種響應性,Windows 提供了一種非同步方法,名為 SyndicationClient.RetrieveFeedAsync,用來下載資料流。

這裡的範例是透過呼叫非同步方法 SyndicationClient.RetrieveFeedAsync 並等待結果,從部落格取得部落格文章清單。

// Put the keyword async on the declaration of the event handler.
private async void Button_Click_1(object sender, RoutedEventArgs e)
{

    Windows.Web.Syndication.SyndicationClient client = new SyndicationClient();

    Uri feedUri
        = new Uri("http://windowsteamblog.com/windows/b/windowsexperience/atom.aspx");

    try
    {
        SyndicationFeed feed = await client.RetrieveFeedAsync(feedUri);

        // The rest of this method executes after await RetrieveFeedAsync completes.
        rssOutput.Text = feed.Title.Text + Environment.NewLine;

        foreach (SyndicationItem item in feed.Items)
        {
            rssOutput.Text += item.Title.Text + ", " +
                             item.PublishedDate.ToString() + Environment.NewLine;
        }
    }
    catch (Exception ex)
    {
        // Log Error.
        rssOutput.Text =
            "I'm sorry, but I couldn't load the page," +
            " possibly due to network problems." +
            "Here's the error message I received: "
            + ex.ToString();
    }
}
' Put the keyword Async on the declaration of the event handler.
Private Async Sub Button_Click_1(sender As Object, e As RoutedEventArgs)
    Dim client As New Windows.Web.Syndication.SyndicationClient()
    Dim feedUri As New Uri("http://windowsteamblog.com/windows/b/windowsexperience/atom.aspx")

    Try
        Dim feed As SyndicationFeed = Await client.RetrieveFeedAsync(feedUri)

        ' The rest of this method executes after the await operation completes.
        rssOutput.Text = feed.Title.Text & vbCrLf

        For Each item In feed.Items
            rssOutput.Text += $"{item.Title.Text}, {item.PublishedDate.ToString()}{vbCrLf}"
        Next

    Catch ex As Exception
        ' Log Error.
        rssOutput.Text = "I'm sorry, but I couldn't load the page," &
                         " possibly due to network problems." &
                         "Here's the error message I received: " &
                          ex.ToString()
    End Try

End Sub

這個例子有幾個重要點。 首先,該行 SyndicationFeed feed = await client.RetrieveFeedAsync(feedUri) 在呼叫非同步方法 RetrieveFeedAsync 時使用了 await 運算子。 你可以把 wait 運算子想像成告訴編譯器你正在呼叫非同步方法,這會讓編譯器多做一些工作,讓你不用再做。 接著,事件處理程序的宣告包含關鍵字 async。 你必須在使用 wait 運算子的方法宣告中包含此關鍵字。

在這個主題中,我們不會詳細說明編譯器如何運用 wait 運算子,而是來看看你的應用程式如何做到非同步且有回應性。 想想使用同步程式碼會發生什麼事。 例如,假設有一個稱為 SyndicationClient.RetrieveFeed 同步的方法。 (沒有這種方法,但試想有。)如果你的應用程式包含了行 SyndicationFeed feed = client.RetrieveFeed(feedUri),而不是 SyndicationFeed feed = await client.RetrieveFeedAsync(feedUri),應用程式的執行會停止,直到返回值 RetrieveFeed 可用為止。 而在你的應用程式等待方法完成時,它無法回應其他事件,例如另一個 點擊 事件。 也就是說,你的應用程式會被封鎖,直到 RetrieveFeed 退貨。

但如果你呼叫 client.RetrieveFeedAsync,該方法會啟動檢索並立即回傳。 當你使用 await 搭配 RetrieveFeedAsync 時,應用程式會暫時退出事件處理程序。 接著在 RetrieveFeedAsync 非同步執行時,它能處理其他事件。 這讓應用程式能對使用者保持反應。 當 RetrieveFeedAsync 完成且 SyndicationFeed 可用時,應用程式基本上會從中斷處重新進入事件處理程序,接著 SyndicationFeed feed = await client.RetrieveFeedAsync(feedUri)完成剩餘的方法。

使用 wait 運算子的好處是,程式碼看起來和用虛數 RetrieveFeed 方法時的程式碼差不多。 有幾種方式可以在 C# 或 Visual Basic 中不使用 await 運算子來撰寫非同步程式碼,但產生的程式碼往往會著重於非同步執行的機制。 這使得非同步程式碼難以撰寫、理解困難,也難以維護。 使用 await 運算子,你可以享受非同步應用程式的好處,而不會讓程式碼變得複雜。

非同步 API 的回傳類型與結果

如果你點了 RetrieveFeedAsync 的連結,你可能會注意到 RetrieveFeedAsync 的回傳類型不是 SyndicationFeed。 相反地,回傳類型為 IAsyncOperationWithProgress<SyndicationFeed, RetrievalProgress>。 從原始語法來看,非同步 API 會回傳一個包含結果的物件。 雖然將非同步方法視為可等待是常見且有時有用的,但 等待 運算子實際上是對方法的回傳值操作,而非方法本身。 當你套用 wait 運算子時,得到的結果是呼叫該方法回傳物件的 GetResult 。 在這個例子中, SyndicationFeedRetrieveFeedAsync.GetResult() 的結果。

使用非同步方法時,你可以檢查簽名,看看等待方法回傳值後會得到什麼。 所有非同步 API 都會回傳以下其中一種類型:

非同步方法的結果型態與 TResult 型別參數相同。 沒有 的 TResult 類型沒有結果。 你可以把結果視為 無效。 在 Visual Basic 中,程序相當於具有回傳型的方法。

此表提供了非同步方法的範例,並列出每種方法的回傳類型與結果類型。

非同步方法 回傳類型 結果類型
SyndicationClient.RetrieveFeedAsync IAsync 操作與進度<同步 訂閱、檢索 進展> 聯合發佈摘要
FileOpenPicker.PickSingleFileAsync IAsyncOperation<儲存檔案> 儲存檔案
XmlDocument.SaveToFileAsync IAsyncAction
InkStrokeContainer.LoadAsync IAsyncActionWithProgress<UInt64>
DataReader.LoadAsync DataReaderLoadOperation 是一個自訂的結果類別,實作 IAsyncOperation<UInt32> UInt32

Note

FileOpenPicker 而其他選擇器 API 則需要在 WinUI 3 桌面應用程式中初始化視窗控制碼(HWND)。 在呼叫 PickSingleFileAsync之前,先呼叫 WinRT.Interop.InitializeWithWindow.Initialize(picker, hwnd),其中 hwnd 來自 WindowNative.GetWindowHandle(this)。 請參考 需要視窗句柄(HWND)的 Display WinUI 3 選擇器 API

.NET 中定義的非同步方法(針對 Windows 應用程式)具有回傳類型 TaskTask<TResult>。 回傳 Task 的方法類似於回傳 IAsyncAction 的非同步方法。 在每種情況下,非同步方法的結果都是 無效的。 回傳類型 Task<TResult>IAsyncOperation<TResult> 類似,因為非同步方法執行任務時的結果與類型參數的類型 TResult 相同。

處理錯誤

當你使用 wait 運算子從非同步方法取得結果時,可以使用 try/catch 區塊來處理非同步方法中發生的錯誤,就像同步方法一樣。 前述範例將 RetrieveFeedAsync 方法包裹並 等待 操作置於 嘗試/捕捉 區塊中,以處理異常拋出時的錯誤。

當非同步方法呼叫其他非同步方法時,任何導致異常的非同步方法都會被傳播到外部方法。 這表示你可以在最外層的方法上放置 try/catch 區塊,來捕捉巢狀非同步方法的錯誤。 這和你在同步方法中捕捉例外狀況的方式類似。 不過,你不能在 catch 區塊中使用 await

Tip

你可以在 catchfinally blocks 裡使用 wait(自 C# 6.0 起支援)。

摘要與後續步驟

我們在這裡展示的呼叫非同步方法的模式,是當你在事件處理程序中呼叫非同步 API 時,最簡單的方式。 當您在傳回 void 的覆寫方法中,或在 Visual Basic 中傳回 Sub 的覆寫方法中呼叫非同步方法時,也可以使用這個模式。

當你遇到非同步方法時,重要的是要記住:

  • 依慣例,非同步方法的名稱以「Async」結尾。
  • 任何使用 wait 運算子的方法都必須以 async 關鍵字標示其宣告。
  • 當應用程式找到 wait 運算子時,應用程式會持續回應使用者互動,而非同步方法則會繼續執行。
  • 等待非同步方法回傳的值會回傳一個包含結果的物件。 在大多數情況下,有用的是返回值中包含的結果,而非返回值本身。 你可以透過查看非同步方法的回傳型別來找到結果中包含的值型別。
  • 使用非同步 API 和 非同步 模式通常是提升應用程式反應速度的一種方法。

本主題的範例輸出的文字長得像這樣。

Windows Experience Blog
PC Snapshot: Sony VAIO Y, 8/9/2011 10:26:56 AM -07:00
Tech Tuesday Live Twitter #Chat: Too Much Tech #win7tech, 8/8/2011 12:48:26 PM -07:00
Windows 7 themes: what's new and what's popular!, 8/4/2011 11:56:28 AM -07:00
PC Snapshot: Toshiba Satellite A665 3D, 8/2/2011 8:59:15 AM -07:00
Time for new school supplies? Find back-to-school deals on Windows 7 PCs and Office 2010, 8/1/2011 2:14:40 PM -07:00
Best PCs for blogging (or working) on the go, 8/1/2011 10:08:14 AM -07:00
Tech Tuesday – Blogging Tips and Tricks–#win7tech, 8/1/2011 9:35:54 AM -07:00
PC Snapshot: Lenovo IdeaPad U460, 7/29/2011 9:23:05 AM -07:00
GIVEAWAY: Survive BlogHer with a Sony VAIO SA and a Samsung Focus, 7/28/2011 7:27:14 AM -07:00
3 Ways to Stay Cool This Summer, 7/26/2011 4:58:23 PM -07:00
Getting RAW support in Photo Gallery & Windows 7 (…and a contest!), 7/26/2011 10:40:51 AM -07:00
Tech Tuesdays Live Twitter Chats: Photography Tips, Tricks and Essentials, 7/25/2011 12:33:06 PM -07:00
3 Tips to Go Green With Your PC, 7/22/2011 9:19:43 AM -07:00
How to: Buy a Green PC, 7/22/2011 9:13:22 AM -07:00
Windows 7 themes: the distinctive artwork of Cheng Ling, 7/20/2011 9:53:07 AM -07:00