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

通用 Windows 平台 (UWP) 包含許多非同步 API,確保您的應用程式在執行可能需要較長時間的工作時能保持回應。 本主題討論如何在 C# 或 Microsoft Visual Basic 中使用 UWP 的非同步方法。

非同步 API 可讓您的應用程式無需等待大型作業完成後再繼續執行。 例如,從網際網路下載資訊的應用程式可能會花費幾秒鐘的時間來等待資訊到達。 如果您使用同步方法來擷取訊息,應用程式將會阻塞,直到該方法傳回為止。 該應用程式不會回應使用者互動,並且由於它似乎沒有回應,使用者可能會感到沮喪。 透過提供非同步 API,UWP 有助於確保應用程式在執行長時間作業時會持續回應使用者。

UWP 中的大多數非同步 API 沒有同步對應項,因此需要確保了解如何在通用 Windows 平台 (UWP) 應用程式中透過 C# 或 Visual Basic 使用非同步 API。 以下我們示範如何呼叫 UWP 的非同步 API。

使用非同步 API

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

假設您有一個應用程式,其列出自某個位置的部落格文章標題。 該應用程式有一個按鈕,使用者點擊該按鈕即可取得標題。 標題會顯示在 TextBlock 中。 當使用者點擊按鈕時,應用程式必須在等待部落格網站資訊時保持回應。 為了確保此回應性,UWP 提供非同步方法 SyndicationClient.RetrieveFeedAsync 來下載摘要。

此處的範例透過呼叫非同步方法 SyndicateClient.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) 行使用 await 運算子來呼叫非同步方法 RetrieveFeedAsync。 您可以將 await 運算子視為告知編譯器您正在呼叫非同步方法,這會導致編譯器執行一些額外的工作,因此您不需要這麼做。 接下來,事件處理程序的宣告會包括關鍵字 async。 您必須在使用 await 運算子之任何方法的方法宣告中包含此關鍵字。

在本主題中,我們不會詳細介紹編譯器對 await 運算子執行的作業,但讓我們檢查一下您的應用程式會執行哪些動作,以使其具有非同步性和回應性。 考慮一下使用同步程式碼時會發生什麼。 例如,假設有一個名為 SyndicationClient.RetrieveFeed 的同步方法。 (沒有這樣的方法,但假設有。) 如果您的應用程式包含 SyndicationFeed feed = client.RetrieveFeed(feedUri) 行而不是 SyndicationFeed feed = await client.RetrieveFeedAsync(feedUri),則應用程式將停止執行,直到 RetrieveFeed 的傳回值可用。 當您的應用程式等待該方法完成時,它無法回應任何其他事件,例如另一個 Click 事件。 也就是說,您的應用程式會阻塞,直到 RetrieveFeed 傳回為止。

但如果您呼叫 client.RetrieveFeedAsync,該方法將啟動擷取並立即傳回。 當您將 awaitRetrieveFeedAsync 搭配使用時,應用程式會暫時退出事件處理程序。 然後,它可以在 RetrieveFeedAsync 以非同步方式執行時,處理其他事件。 這可以讓應用程式對使用者做出回應。 當 RetrieveFeedAsync 完成且 SyndicateFeed 可用時,應用程式會在 SyndicationFeed feed = await client.RetrieveFeedAsync(feedUri) 後重新進入事件處理程序,並完成該方法的其餘部分。

使用 await 運算子的好處是,程式碼看起來與使用虛構 RetrieveFeed 方法時的程式碼沒有太大區別。 有幾種方法可以在 C# 或 Visual Basic 中撰寫無需 await 運算子的非同步程式碼,但產生的程式碼往往會強調非同步執行的機制。 這使得非同步程式碼難以撰寫、難以理解並且難以維護。 透過使用 await 運算子,您可以享受非同步應用程式的優勢,而無需讓程式碼變得複雜。

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

如果您點擊 RetrieveFeedAsync 的連結,您可能已經發現 RetrieveFeedAsync 的傳回類型不是 SyndicateFeed。 取而代之的,傳回類型會是 IAsyncOperationWithProgress<SyndicationFeed, RetrievalProgress>。 從原始語法來看,非同步 API 會傳回一個包含結果的物件。 雖然將非同步方法視為可等待的做法很常見,有時甚至很有用,但 await 運算子實際上是對方法的傳回值進行動作,而不是對方法本身進行動作。 當您套用 await 運算子時,您傳回的是對該方法傳回物件呼叫GetResult 的結果。 在本範例中,SyndicateFeedRetrieveFeedAsync.GetResult() 的結果。

當您使用非同步方法時,可以檢查簽章以查看在等待該方法傳回的值後將傳回什麼。 UWP 中的所有非同步 API 都會傳回下列其中一種類型:

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

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

非同步方法 傳回類型 結果類型
SyndicationClient.RetrieveFeedAsync IAsyncOperationWithProgress<SyndicationFeed, RetrievalProgress> SyndicationFeed
FileOpenPicker.PickSingleFileAsync IAsyncOperation<StorageFile> StorageFile
XmlDocument.SaveToFileAsync IAsyncAction void
InkStrokeContainer.LoadAsync IAsyncActionWithProgress<UInt64> void
DataReader.LoadAsync DataReaderLoadOperation,這是實作 IAsyncOperation<UInt32> 的自訂結果類別 UInt32

 

NET for UWP 應用程式定義的非同步方法具有傳回類型 Task or Task<TResult>。 傳回 Task 的方法與 UWP 中傳回 IAsyncAction 的非同步方法類似。 在這幾個案例中,非同步方法的結果都是 void。 傳回類型 Task<TResult>IAsyncOperation<TResult> 類似,執行工作時非同步方法的結果與 TResult 類型參數的類型相同。 有關將 .NET for UWP 應用程式和工作的詳細資訊,請參閱 .NET for Windows 執行階段應用程式概觀

處理錯誤

當您使用 await 運算子從非同步方法擷取結果時,可以使用 try/catch 區塊來處理非同步方法中發生的錯誤,就像處理同步方法一樣。 上述範例會將 RetrieveFeedAsync 方法和 await 作業包裝在 try/catch 區塊中,以便在擲回例外狀況時處理錯誤。

當非同步方法呼叫其他非同步方法時,任何導致例外狀況的非同步方法都會傳播到外部方法。 這代表您可以在最外層方法上放置一個 try/catch 區塊來攔截巢狀非同步方法的錯誤。 同樣,這與攔截同步方法例外狀況的方式類似。 但是,您不能在 catch 區塊中使用 await

提示 從 Microsoft Visual Studio 2005 中的 C# 開始,您可以在 catch 區塊中使用 wait

摘要和後續步驟

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

當您在 UWP 中遇到非同步方法時,請務必記住:

  • 按照慣例,非同步方法的名稱以「Async」結尾。
  • 任何使用 await 運算子的方法都必須在其宣告中使用 async 關鍵字進行標記。
  • 當應用程式找到 await 運算子時,應用程式會在執行非同步方法時保持對使用者互動的回應。
  • 等待非同步方法傳回的值會傳回一個包含結果的物件。 在大多數情況下,傳回值中包含的結果才是有用的,而不是傳回值本身。 您可以透過查看非同步方法的傳回類型來找到結果中包含的值的類型。
  • 要提高應用程式回應能力通常會使用非同步 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