Async Await с методом, вызывающим внешний ресурс или возвращающим локальную строку - PullRequest
1 голос
/ 09 апреля 2019

Мне всегда было интересно, правильно ли я выполняю асинхронное ожидание, и я не могу найти ничего, объясняющего мой сценарий, и повлияет ли способ, которым я закодировал мои методы, на мое приложение.

У меня есть метод, который будет вызывать внешний ресурс с помощью HttpClient, если локальная переменная еще не заполнена, если локальная переменная определена, я возвращаю переменную.

Вот пример:

1:  private static string foo;
2:  public static async Task<string> GetFooDataAsync()
3:  {
4:      var needToFetchFoo = string.IsNullOrWhiteSpace(foo);
5:      if (needToFetchFoo)
6:      {
7:          var httpResponse = await myHttpClient.GetFooAsync(params);
8:          foo = httpResponse.data;
9:      }
10:     return foo;
11: }

Моя проблема в строке 10, когда я возвращаю переменную? Должен ли я делать что-то вроде?

return Task.FromResult(foo);

Возвращает ли переменная без Task.FromResult то, что делает асинхронное ожидание, и вызывает проблемы выше в моем стеке вызовов?

Ответы [ 3 ]

4 голосов
/ 09 апреля 2019

Код у вас как в порядке. Когда вы используете async, компилятор уже реализовал всю необходимую сантехнику, чтобы вернуть задачу через сгенерированную реализацию IAsyncStateMachine.

Как вы можете видеть в этом бессмысленном примере здесь

public async Task<bool> DoSomething()
{
    return true;
}

переводит примерно на

[AsyncStateMachine(typeof(<DoSomething>d__0))]
public Task<bool> DoSomething()
{
    <DoSomething>d__0 stateMachine = default(<DoSomething>d__0);
    stateMachine.<>t__builder = AsyncTaskMethodBuilder<bool>.Create();
    stateMachine.<>1__state = -1;
    AsyncTaskMethodBuilder<bool> <>t__builder = stateMachine.<>t__builder;
    <>t__builder.Start(ref stateMachine);
    return stateMachine.<>t__builder.Task;
}

Вы бы использовали Task.FromResult и Task.FromException, если вы не добавили ключевое слово async и выполняете синхронный код.

В этих случаях Task.FromResult возвращает выполненное задание , и Task.FromException добавит исключение к Заданию , как это сделает инфраструктура для исключения , брошенные async методом

public Task<bool> DoSomeInterfaceAsync()
{
   try
   {
      // return a completed task
      return Task.FromResult(DoSomethingThatMightThrow());
   }
   catch (Exception e)
   {
      // Add the exception to the task 
      return Task.FromException<bool>(e);
   }   
}

Также интересно отметить, что в сгенерированном компилятором коде, показанном выше, есть метод MoveNext, который подтверждает Task.FromResult и Task.FromException, и его можно увидеть ниже с SetException и SetException соответственно

private void MoveNext()
{
    bool result;
    try
    {
        result = true;
    }
    catch (Exception exception)
    {
        <>1__state = -2;
        <>t__builder.SetException(exception);
        return;
    }
    <>1__state = -2;
    <>t__builder.SetResult(result);
}

AsyncTaskMethodBuilder.SetException

Помечает задачу как невыполненную и привязывает указанное исключение к задача.

AsyncTaskMethodBuilder.SetResult

Помечает задачу как успешно выполненную.

2 голосов
/ 09 апреля 2019

Логика кеширования немного ошибочна.Во время выполнения http-запроса foo остается незаполненным, и если в течение этого времени другой метод вызывает GetFooDataAsync(), http-запрос будет повторен.В идеале кэш должен заполняться только один раз.

Чтобы обеспечить один и только один вызов задачи, кэшируйте саму задачу, а не результат, и просто ожидайте ее всякий раз, когда вы хотите получить результат.Второе или третье ожидание больше не будет вызывать метод, оно просто получит доступ к существующему результату.

private static Task<string> foo = null;

private static async Task<string> GetFooDataAsync()
{
    async Task<string> GetFooDataInternal()
    {
        var response = await myHttpClient.GetFooAsync();
        return response.data;
    }

    if (foo == null) foo = GetFooDataInternal();
    return await foo;
}
0 голосов
/ 09 апреля 2019

Если вы пометили метод как async, вы должны вернуть результат, а не Task или Task<T>. Об остальном позаботится компилятор.

Task.FromResult обычно требуется, когда метод не помечен как async, но тип возвращаемого метода - Task или Task<T>.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...