सिंक्रोनस C# कोड में, एक्सेप्शन कॉल स्टैक तक प्रसारित होते हैं जब तक वे एक उपयुक्त catch ब्लॉक तक नहीं पहुंच जाते जो एक्सेप्शन को संभाल सके। हालांकि, एसिंक्रोनस मेथड्स में एक्सेप्शन हैंडलिंग सीधी नहीं है।
C# में एक एसिंक्रोनस मेथड के तीन प्रकार के रिटर्न मान हो सकते हैं: void, Task, और Task<T>। जब Task या Task<T> रिटर्न टाइप वाले एसिंक्रोनस मेथड में एक्सेप्शन होता है, तो एक्सेप्शन ऑब्जेक्ट AggregateException के इंस्टेंस में लपेटा जाता है और Task ऑब्जेक्ट से जुड़ जाता है।
यदि कई एक्सेप्शन फेंके जाते हैं, तो उन सभी को Task ऑब्जेक्ट में संग्रहीत किया जाता है।
समस्या: async void एक्सेप्शन पकड़े नहीं जा सकते
// खतरनाक: async void - एक्सेप्शन क्रैश करेगा एप्लिकेशन
public async void BadAsyncVoid()
{
throw new InvalidOperationException("Oops!");
}
// कॉलर इसे पकड़ नहीं सकता
try
{
BadAsyncVoid(); // एक्सेप्शन यहां नहीं पकड़ा जाएगा!
}
catch (Exception ex)
{
// कभी नहीं पहुंचेगा
}
समाधान: async Task का उपयोग करें
// सही: async Task - एक्सेप्शन Task में कैप्चर होता है
public async Task GoodAsyncTask()
{
throw new InvalidOperationException("Oops!");
}
// कॉलर इसे पकड़ सकता है
public async Task Caller()
{
try
{
await GoodAsyncTask();
}
catch (InvalidOperationException ex)
{
Console.WriteLine($"Caught: {ex.Message}");
}
}
उदाहरण: सही एक्सेप्शन हैंडलिंग
static async Task Main(string[] args)
{
await DoSomething();
Console.ReadLine();
}
public static async Task Foo()
{
throw new ArgumentNullException("Parameter cannot be null");
}
public static async Task DoSomething()
{
try
{
await Foo();
}
catch (ArgumentNullException ex)
{
Console.WriteLine($"Caught: {ex.Message}");
Console.WriteLine($"Stack trace: {ex.StackTrace}");
}
}
आउटपुट
Caught: Parameter cannot be null
Stack trace: at Program.Foo() in ...\Program.cs:line 37
at Program.DoSomething() in ...\Program.cs:line 44
एकाधिक एक्सेप्शन हैंडलिंग (Task.WhenAll)
public static async Task HandleMultipleTasks()
{
var task1 = Task.Run(() => { throw new ArgumentException("Task 1 failed"); });
var task2 = Task.Run(() => { throw new InvalidOperationException("Task 2 failed"); });
try
{
await Task.WhenAll(task1, task2);
}
catch (Exception ex) when (ex is AggregateException aggEx)
{
foreach (var inner in aggEx.InnerExceptions)
{
Console.WriteLine($"Inner exception: {inner.GetType().Name} - {inner.Message}");
}
}
catch (Exception ex)
{
Console.WriteLine($"Single exception: {ex.Message}");
}
}
async void के वैध उपयोग
- इवेंट हैंडलर्स (जैसे
button_Click) - टॉप-लेवल एंट्री पॉइंट्स जहां कोई कॉलर नहीं है
सर्वोत्तम अभ्यास
- लगभग हमेशा
async Taskलौटाएं -async voidसे बचें - इवेंट हैंडलर्स के लिए
async voidस्वीकार्य है - टॉप-लेवल प्रोग्राम में
async Task Main()का उपयोग करें - एक्सेप्शन को अवेट करें -
Task.Runबिना अवेट के न करें - ConfigureAwait(false) लाइब्रेरी कोड में उपयोग करें
मूल कोड में समस्या
मूल उदाहरण static async Task Main का उपयोग करता है जो C# 7.1+ में ठीक है, लेकिन Task.Delay(1000) को अवेट नहीं करता - यह केवल टास्क बनाता है और उसे अनदेखा करता है। सही उपयोग: await Task.Delay(1000)।