Ответ
Лучший способ справиться с вашим сценарием - использовать async void
.
Я рекомендую сначала прочитать раздел Explanation
ниже, чтобы полностью ознакомиться с лучшими практиками по async void
.
public MyConstructor()
{
ExecuteAsyncMethods();
}
async void ExecuteAsyncMethods()
{
try
{
await OnLoadPrometDanKorisnikDatum(KorisnikID, PomocnaDnDDatnaDat, DatumVrednost);
await OnLoadPrometNedelja(KorisnikID, PomocnaDnDDatnaDatNedelja);
}
catch(Exception e)
{
//Handle Exception
}
}
Объяснение
Многим C# разработчикам преподают "Никогда не используйте async void
", но это один из немногих вариантов использования.
Да async void
может быть опасным, и вот почему:
- Не может
await
async avoid
метод - Может привести к условиям гонки
- Трудно поймать
Exception
, брошенный async void
методами - Например, следующий
try/catch
блок не будет ловить Exception
брошенный здесь:
public MyConstructor()
{
try
{
//Cannot await `async void`
AsyncVoidMethodWithException();
}
catch(Exception e)
{
//Will never catch the `Exception` thrown in `AsyncVoidMethodWithException` because `AsyncVoidMethodWithException` cannot be awaited
}
//code here will be executing by the time `AsyncVoidMethodWithException` throws the exception
}
async void AsyncVoidMethodWithException()
{
await Task.Delay(2000);
throw new Exception();
}
При этом, пока мы заключаем содержимое всего нашего async void
в try/catch
блок, мы сможем перехватить исключение, например:
public MyConstructor()
{
AsyncVoidMethodWithException();
}
async void AsyncVoidMethodWithException()
{
try
{
await Task.Delay(2000);
throw new Exception();
}
catch(Exception e)
{
//Exception will be caught and successfully handled
}
}
SafeFireAndForget
Я создал библиотеку, чтобы помочь с этим, и ее дополнительное преимущество заключается в том, что она избегает написания async void
кода, который может потенциально использоваться будущими разработчиками в будущем.
Это открытый код и также доступен на NuGet:
SafeFireAndForget
SafeFireAndForget
позволяет нам безопасно выполнить Task
, не блокируя вызывающий поток и не дожидаясь его завершения sh перед перемещением к следующей строке кода.
Ниже приведена упрощенная версия SafeFireAndForget
, которую вы можете добавить в свой проект.
Однако я рекомендую скопировать / вставить его полный исходный код или добавление пакета NuGet в вашу библиотеку, чтобы получить более надежную реализацию
public static async void SafeFireAndForget<TException>(this Task task, Action<TException> onException = null, bool continueOnCapturedContext = false) where TException : Exception
{
try
{
await task.ConfigureAwait(continueOnCapturedContext);
}
catch (TException ex) when (onException != null)
{
onException(ex);
}
}
Использование SafeFireAndForget
Чтобы использовать SafeFireAndForget
, добавьте его в свой вызов метода выглядит так:
OnLoadPrometDanKorisnikDatum(KorisnikID, PomocnaDnDDatnaDat, DatumVrednost).SafeFireAndForget();
OnLoadPrometNedelja(KorisnikID, PomocnaDnDDatnaDatNedelja).SafeFireAndForget();
Для обработки любого Exception
, брошенного этим Task
, используйте onException
. Вот пример, который печатает Exception
на консоли отладки:
OnLoadPrometDanKorisnikDatum(KorisnikID, PomocnaDnDDatnaDat, DatumVrednost).SafeFireAndForget(ex => Debug.WriteLine(ex));
OnLoadPrometNedelja(KorisnikID, PomocnaDnDDatnaDatNedelja).SafeFireAndForget(ex => Debug.WriteLine(ex));