我一直想知道我是否在正确地进行异步等待,是否找不到任何解释我的情况的信息,以及我对方法的编码方式是否会影响我的应用程序。
我有一个方法,如果尚未填充局部变量,则将使用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返回变量是否会影响异步等待的操作并在我的调用堆栈中引起更高的问题?
答案 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.FromException
和async
。
在这种情况下,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.FromResult
和Task.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
,则必须返回结果,而不是Task
或Task<T>
。编译器将负责其余的工作。
Task.FromResult
通常在方法未标记为async
但方法的返回类型为Task
或Task<T>
时是必需的。