TAP Task 產生例外異常 Exception 發生同步或者非同步的探討
我們知道,若直接使用執行緒 Thread 來設計非同步 Asynchronous 處理需求,我們是無法在啟動該執行緒那哩,透過 try { someThread.Star() } catch {...} 這樣的程式碼,捕捉到任何在該執行緒中發生的任何例外異常,您僅能夠在該執行緒方法內自行捕捉相關例外異常事件的產生。
而當我們使用 Task 類別協助我們進行非同步應用程式碼開發的時候,必定會遇到在非同步方法內會發生例外異常 Exception 的問題,而我們必須知道,當您使用 Task 進行設計的時候,在呼叫端是可以捕捉到這些非同步方法內產生的例外異常事件;可以這樣做到的原因在於當我們進行 Task 類別為基礎的非同步方法設計的時候,當有例外異常發生的話,當時發生的例外事件會儲存在 Task 物件內,我們可以透過 Task.Exception 這個物件來取得當時發生例外異常的物件。
不過,當進行 Task 非同步方法呼叫的時候,我們通常會使用 await 關鍵字來等候這個非同步工作完成,因此,當有使用 await 等候非同步工作的時候,若非同步方法內有例外異常產生,這個 await 將會在呼叫非同步方法的呼叫端,再次丟出這個例外異常出來,因此,我們就可以捕捉到。然而,若我們使用類似這樣的敘述
Task<string> task2 = GetContentAsync("https://www.google.com");
與 var content = await task2;
這兩個敘述的時候,會在呼叫非同方法的呼叫端產生例外異常的敘述將會是後者。
另外一個注意事項,那就是在 Task 為基礎的非同步方法內,在該非同步方法內尚未呼叫任何其他非同步方法的時候,例如,我們可能會在非同步工作方法的最前面,要檢查使用者是否有傳入適當的參數到這個非同步方法內,若沒有,我們就會丟出例外異常出來;不過,因為該非同步工作方法尚未進入呼叫與等候其他的非同步工作,因此,我們在呼叫端無需使用 await 關鍵字,當取得這個非同步工作的 Task 物件時候,就會產生了例外異常。我們以這兩行程式碼敘述為例
Task<string> task1 = GetContentAsync(null);
與 var content = await task1;
,若符合剛剛敘述的情境,呼叫端的例外異常將會發生在前者敘述。想要測試這樣的過程,請將底下範例程式碼的 #region 在執行非同步方法時發生例外異常
到 #endregion
之間的敘述註解起來,並且在 Task<string> task1 = GetContentAsync(null);
與 Console.WriteLine(exceptionSync.Message);
這兩個敘述上設定一個除錯中斷點;現在,讓我們來實際執行這個範例程式碼。
當程式執行後,會立即停留在
Task<string> task1 = GetContentAsync(null);
方法上,此時,尚未進入到任何非同步工作方法內,現在,我們逐步執行來進行除錯,因為我們傳入一個空值到這個非同步工作內,因此,在這個非同步工作方法內在為呼叫讓和其他非同步工作的時候,就直接產生了例外異常,所以,Task<string> task1 = GetContentAsync(null);
這行敘述雖然僅是取得一個非同步工作 Task 物件,並且我們還沒有使用 await 關鍵字來等候這個非同步工作,就已經在呼叫端產生的例外異常。
現在,我們來看看另外一個情境,請將
#region 在執行非同步方法前發生例外異常
與 #endregion
之間的程式碼註解起來,並且將 #region 在執行非同步方法時發生例外異常
到 #endregion
之間的敘述解除註解,並且在 Task<string> task2 = GetContentAsync("https://www.google.com");
與 Console.WriteLine(exceptionSync.Message);
這兩個敘述上設定一個除錯中斷點;讓我們來執行這段測試程式碼。
當程式執行後,程式就會停在 第一個中斷點
Task<string> task2 = GetContentAsync("https://www.google.com");
上,現在我們繼續逐步執行這個除錯程式,我們發現到竟然沒有在呼叫端產生任何例外異常,這是因為當我們執行這個 GetContentAsync 非同步工作方法的時候,是在該方法內呼叫其他非同步工作的時候,才會產生例外異常,因此,我們程式碼可以繼續執行到這個敘述上 var content = await task2;
。不過,當我們繼續執行等候這個非同步工作方法的時候,就發生了例外異常,這是因為此非同步工作的 Task 物件內 Exception 屬性上有例外異常物件,並且我們處於等候非同步工作情境下。class Program
{
static async Task Main(string[] args)
{
#region 在執行非同步方法前發生例外異常
try
{
Task<string> task1 = GetContentAsync(null);
var content = await task1;
}
catch (Exception exceptionSync)
{
Console.WriteLine(exceptionSync.Message);
}
#endregion
#region 在執行非同步方法時發生例外異常
try
{
Task<string> task2 = GetContentAsync("https://www.google.com");
var content = await task2;
}
catch (Exception ex)
{
Console.WriteLine(ex.Message);
}
#endregion
Console.WriteLine("Press any key for continuing...");
Console.ReadKey();
}
static Task<string> GetContentAsync(string url)
{
if (url == null)
throw new ArgumentException($"{nameof(url)} 為空值");
return GetContentInternalAsync(url);
}
static async Task<string> GetContentInternalAsync(string url)
{
var content = await new HttpClient().GetStringAsync(url);
throw new HttpRequestException("模擬存取網路異常");
return content;
}
}
關於 Xamarin 在台灣的學習技術資源
歡迎加入 Xamarin 實驗室 粉絲團,在這裡,將會經常性的貼出各種關於 Xamarin / Visual Studio / .NET 的相關消息、文章、技術開發等文件,讓您可以隨時掌握第一手的 Xamarin 方面消息。
歡迎加入 Xamarin.Forms @ Taiwan,這是台灣的 Xamarin User Group,若您有任何關於 Xamarin / Visual Studio / .NET 上的問題,都可以在這裡來與各方高手來進行討論、交流。
Xamarin 實驗室 部落格 是作者本身的部落格,這個部落格將會專注於 Xamarin 之跨平台 (Android / iOS / UWP) 方面的各類開技術探討、研究與分享的文章,最重要的是,它是全繁體中文。
Xamarin.Forms 系列課程 想要快速進入到 Xamarin.Forms 的開發領域,學會各種 Xamarin.Forms 跨平台開發技術,例如:MVVM、Prism、Data Binding、各種 頁面 Page / 版面配置 Layout / 控制項 Control 的用法等等,千萬不要錯過這些 Xamarin.Forms 課程