Зазвичай асинхронні методи C# мають значення, яке представляє відповідну задачу, наприклад, Task або Task
Якщо під час виконання асинхронного методу виникає необроблена помилка, вона передається назад у місце, де очікується ця задача.
async Task Foo()
{
await Task.Sleep(1);
throw new Exception();
}
async Task Bar()
{
var task = Foo();
try
{
await task;
}
catch
{
// Помилку, що виникла в Foo, можна обробити тут.
}
}
А що буде, якщо не дочекатися завершення задачі? Помилка залишиться необробленою, і головний потік буде продовжувати виконання без змін. Нічого не станеться.
Однак ситуація змінюється, якщо асинхронний метод не має повернутого значення. Оскільки помилка не збирається в Task або подібному, вона передається назад у головний потік і призводить до зупинки всієї програми.
async void Foo()
{
await Task.Sleep(1);
throw new Exception(); // Програма зупиняється на цьому місці.
}
void Bar()
{
try
{
Foo();
}
catch
{
// Тут помилка не буде зібрана.
}
}
Це стає особливо проблематичним, коли метод викликається ззовні програми.
Припустимо, що метод Foo з прикладу вище є частиною окремої бібліотеки. Автор методу Bar може не знати, що Foo — асинхронний метод, і спробує обробити помилку, як у наведеному коді.
В результаті помилка не буде оброблена в блоці catch, і програма зупиниться. Якщо не знати про таку особливість, пошук причини проблеми може зайняти досить багато часу.
Перекладено з: Async void와 예외 처리