通过调用外部资源或返回本地字符串的方法进行异步等待

时间:2019-04-09 05:28:16

标签: c# async-await

我一直想知道我是否在正确地进行异步等待,是否找不到任何解释我的情况的信息,以及我对方法的编码方式是否会影响我的应用程序。

我有一个方法,如果尚未填充局部变量,则将使用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 个答案:

答案 0 :(得分:4)

您拥有的代码就可以了。当您使用async时,编译器已经实现了所有必要的管道,以通过IAsyncStateMachine compiler生成的实现返回 task

在这个荒谬的示例中,您可以看到here

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.FromExceptionasync

在这种情况下,Task.FromResult返回完成的任务 Task.FromException会将 exception 添加到 Task 就像框架对在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.FromResultTask.FromException辩护,可以在下面用SetException和{分别为{1}}

SetException

AsyncTaskMethodBuilder.SetException

  

将任务标记为失败,并将指定的异常绑定到   任务。

AsyncTaskMethodBuilder.SetResult

  

将任务标记为成功完成。

答案 1 :(得分:2)

缓存逻辑略有缺陷。在进行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;
}

答案 2 :(得分:0)

如果将方法标记为async,则必须返回结果,而不是TaskTask<T>。编译器将负责其余的工作。

Task.FromResult通常在方法未标记为async但方法的返回类型为TaskTask<T>时是必需的。