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 。 在這個例子中, SyndicationFeed 是 RetrieveFeedAsync.GetResult() 的結果。
使用非同步方法時,你可以檢查簽名,看看等待方法回傳值後會得到什麼。 所有非同步 API 都會回傳以下其中一種類型:
- IAsyncOperation<TResult>
- 具有進度的非同步作業<TResult, TProgress>
- IAsyncAction
- IAsyncActionWithProgress<TProgress>
非同步方法的結果型態與 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 應用程式)具有回傳類型 Task 或 Task<TResult>。 回傳 Task 的方法類似於回傳 IAsyncAction 的非同步方法。 在每種情況下,非同步方法的結果都是 無效的。 回傳類型 Task<TResult> 與 IAsyncOperation<TResult> 類似,因為非同步方法執行任務時的結果與類型參數的類型 TResult 相同。
處理錯誤
當你使用 wait 運算子從非同步方法取得結果時,可以使用 try/catch 區塊來處理非同步方法中發生的錯誤,就像同步方法一樣。 前述範例將 RetrieveFeedAsync 方法包裹並 等待 操作置於 嘗試/捕捉 區塊中,以處理異常拋出時的錯誤。
當非同步方法呼叫其他非同步方法時,任何導致異常的非同步方法都會被傳播到外部方法。 這表示你可以在最外層的方法上放置 try/catch 區塊,來捕捉巢狀非同步方法的錯誤。 這和你在同步方法中捕捉例外狀況的方式類似。 不過,你不能在 catch 區塊中使用 await。
Tip
你可以在 catch 和 finally 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