Ожидание или Задача. FromResult
У меня есть одна служба, скажем,
public interface ISomeService
{
Task<bool> DoSomeExpensiveCheckAsync(string parameter);
}
И у меня есть этот класс, чтобы использовать службу. Он просто должен выполнить несколько простых нулевых проверок, а затем вернуть ответ службы.
public class SomeServiceConsumer
{
private readonly ISomeService _serviceClient;
public SomeServiceConsumer(ISomeService serviceClient)
{
_serviceClient = serviceClient;
}
public async Task<bool> DoSomething1Async(string someParameter)
{
if (string.IsNullOrWhiteSpace(someParameter))
{
return false;
}
return await _serviceClient.DoSomeExpensiveCheckAsync(someParameter);
}
//No async or await keywords
public Task<bool> DoSomething2Async(string someParameter)
{
if (string.IsNullOrWhiteSpace(someParameter))
{
return Task.FromResult(false);
}
return _serviceClient.DoSomeExpensiveCheckAsync(someParameter);
}
}
Должен ли я делать DoSomething1Async
или DoSomething2Async
?
В соответствии с этим ответом я не должен обертывать ненужным await
, но тогда я должен использовать Task.FromResult(false)
для короткого вызова, как в DoSomething2Async
Но в соответствии с этим ответом есть случаи с операторами try/catch
и using
, где я должен был await
перед возвратом.
Правильно ли я говорю, что
-
Если мне нужно использовать try/catch
или using
, тогда я должен await
-
В противном случае не await
, если вы только вернетесь. И используйте Task.FromResult
для короткого замыкания
Мне нравится DoSomething1Async
больше, и я хочу сделать это везде, если кто-то скажет, что это не имеет значения:).
Ответы
Ответ 1
Если вы беспокоитесь об этом, кешируйте Task
:
static readonly Task<bool> falseTask = Task.FromResult(false);
Ключевое слово async
также включает исключения в возвращенном Task
, а также правильную трассировку стека. Это компромисс, безопасность поведения для перформанса.
Давайте рассмотрим разностные сценарии, в которых каждый будет отличаться:
async Task UseSomething1Async(string someParameter)
{
// if IsNullOrWhiteSpace throws an exception, it will be wrapped in
// the task and not thrown here.
Task t1 = DoSomething1Async(someParameter);
// rather, it'll get thrown here. this is best practice,
// it what users of Task-returning methods expect.
await t1;
// if IsNullOrWhiteSpace throws an exception, it will
// be thrown here. users will not expect this.
Task t2 = DoSomething2Async(someParameter);
// this would never have been reached.
await t2;
}
Просто проиллюстрируем здесь пункт - IsNullOrWhiteSpace
на самом деле не бросает никаких исключений по какой-либо причине.
Что касается трассировки стека, то трассировка стека асинхронов определяется тем, где вы await
. Нет await
означает, что метод исчезнет из трассировки стека.
Скажите DoSomeExpensiveCheckAsync
выдает исключение. В случае DoSomething1Async
трассировка стека будет выглядеть как caller -> DoSomething1Async -> DoSomeExpensiveCheckAsync
.
В случае DoSomething2Async
трассировка стека будет выглядеть как caller -> DoSomeExpensiveCheckAsync
. В зависимости от сложности вашего кода это может затруднить отладку.
На практике я обычно буду только возвращать Task
, если бы я знал, что перед ним не будут исключены исключения, и если имя метода было просто перегрузкой, пересылающей другую перегрузку. Всегда есть исключения из этого правила, обязательно должны быть места, которые вы хотите максимизировать производительность. Просто выберите и выберите тщательно, поймите, что вы можете сделать жизнь вас и вашего пользователя более трудной.
Ответ 2
На самом деле это не имеет значения.. Если вам удобнее всегда маркировать Task
-приведение методов с помощью ключевого слова async
, то используйте DoSomething1
.
Как вы сказали, это компромисс:
-
DoSomething2
не создает конечный автомат, необходимый для метода async
, и поэтому он немного быстрее (но разница в основном незначительна).
-
С другой стороны, он может иметь некоторые непредвиденные побочные эффекты в отношении обработки исключений, поскольку в методе async
исключение будет храниться в возвращенном Task
, а в другом случае оно будет выбрано регулярно.