Недавно я наблюдал обзор кода между двумя разработчиками.
Был отправлен следующий код:
public async Task<List<Thing>> GetThings()
{
try
{
var endpoint = $"{Settings.ThingEndpoint}/things";
var response = await HttpClient.GetAsync(endpoint);
return JsonConvert.DeserializeObject<List<Thing>>(await response.Content.ReadAsStringAsync());
}
catch (Exception e)
{
Log.Logger.Error(e.ToString());
return await Task.FromException<List<Thing>>(e);
}
}
Который получил следующие комментарии обзора:
There is absolutely no need to return await Task.FromException>(e), this is something you do when dealing with non awaited task. In this case the catch will capture whatever exception var response = await HttpClient.GetAsync(endpoint); will throw. You should just remove it and catch the exception as is
Я не совсем понимаю, почему бы не использовать Task.FromException в этом случае, поэтому у меня есть следующие вопросы:
Под «методом без асинхронности/ожидания» я подразумеваю метод, который написан не как async Task
, а как Task
, внутри которого вы должны возвращать задачи, например Task.FromResult
или Task.FromException
.
@LasseVågsætherKarlsen Спасибо. Я не понимаю, почему вы используете неасинхронный метод. А почему бы вам не использовать в asyc методе. Можете ли вы объяснить подробнее, пожалуйста?
Рецензент совершенно прав.
Единственная ситуация, которую вы бы использовали Task.FromException
, — это когда вы находитесь в методе, который вы не можете или не будете реализовывать с помощью async
и await
, и вы хотите, чтобы результат задачи был исключением.
Идиотский пример, но тем не менее:
public Task<int> NotReallyAsync()
{
if (new Random().Next(2) == 0)
return Task.FromResult(42);
return Task.FromException<int>(new InvalidOperationException());
}
Итак, давайте разберемся с вашими вопросами один за другим:
Рецензент говорит, что Task.FromException
следует использовать только в методе, отличном от async
/await
, в методе async
/await
вместо этого вы должны просто повторно выдать исключение:
catch (Exception e)
{
Log.Logger.Error(e.ToString());
throw;
}
или если вы реализуете фильтр исключений:
catch (Exception e) when (Log.Logger.ExceptionFilter(e)) { }
Да, рецензент прав.
async
/await
состоит в том, чтобы иметь возможность написать свой метод обычным образом, поэтому напишите обычный оператор throw или обычный блок catch.async
/await
методы, да и только.Task.FromResult выдаст совокупное исключение, которое необходимо проанализировать для дальнейшего повторного генерирования.
Task.FromException обычно используется, когда вы хотите полагаться на состояние задачи, если выполняется известное условие сбоя. Например, если объект имеет значение null, вы знаете, что должны вернуть задачу с ошибкой. Клиент может использовать состояние задачи как неисправной, чтобы показать пользователю соответствующее сообщение. Я изменил код только для того, чтобы рассказать вам на примере.
public async Task<List<Thing>> GetThings()
{
try
{
var endpoint = $"{Settings.ThingEndpoint}/things";
var response = await HttpClient.GetAsync(endpoint);
var obj = JsonConvert.DeserializeObject<List<Thing>>(await response.Content.ReadAsStringAsync());
if (obj==null)
{
return await Task.FromException<List<Thing>>(new NullRefernceException());
}
else
{
}
}
catch (Exception e)
{
Log.Logger.Error(e.ToString());
throw;
}
}
1. Рецензент говорит, что вы должны
throw;
вместо этого (после регистрации) 2: Да, рецензент прав. 3/4.Task.FromException
предназначен для использования в коде библиотеки в неасинхронных/ожидающих методах.