我做了一个httpclient调用,然后想立即开始计算。我不希望计算受到返回响应的干扰。计算最多可能需要 3 秒。
伪代码
{
var data = await GetDataFromCloud();
//do calculations - highly time sensitive!
DoCalc() //A recursive job or nested loop *the response is occurring within this call :(
//I want the response to occur here after the work above has completed :)
}
会在这里锁定工作。 此代码位于 UI 线程上的异步函数内。 DoCalc 是非阻塞的。UI 线程未被阻止。
假设您不需要从GetDataFromCloud()
返回的数据来实际执行计算,您可以从方法中获取任务而不是等待它,进行计算,然后等待它。
像这样:
{
var dataTask = GetDataFromCloud(); // Starts the task and returns it
//do calculations - highly time sensitive!
DoCalc()
var data = await dataTask; // Wait for the task to finish (if it hasn't already)
}
当然,如果计算需要数据,那么您要么需要等待所有数据先下载,要么通过抓取 HttpResponseMessage 并手动读取内容来处理"数据流"。
编辑:
还值得注意的是,如果请求中出现问题(即抛出异常),使用这种方法直到await
才会知道它。
这正是我们想要使用 async-await 的场景!
当您的线程必须空闲地等待其他事情完成时,异步等待很有用。例如,要读取的文件、要执行的数据库查询、要下载的网页。通常,其他人正在执行工作,而您的线程只是在等待工作完成。
您可以"执行其他操作而不是闲置等待"的函数被声明为异步。请参阅读取文件时的Stream.ReadAsync
、执行数据库查询时的Dapper.SQLMapper.QueryAsync
、实体框架中数据库查询的ToListAsync
和FirstOrDefaultAsync
、互联网下载WebClient.DownloadFileAsync
等。
如果调用异步函数,则返回值是Task
而不是void
,或者返回值是Task<TResult>
而不是TResult
。有一个例外:事件处理程序不会生成任务,而是无效。
如果你想从 async-await 中受益,你必须定义你的函数 async 并返回一个Task
或Task<TResult>
调用异步函数时,可以确定此函数中的某个位置是等待。事实上,如果你忘记在某个地方等待,你的编译器会警告你。同样,如果您定义了函数异步但忘记调用另一个异步函数,则会收到警告
调用另一个异步函数后,只要不需要调用的结果,就可以继续工作。一旦您需要结果,您就可以等待任务。
如果您开始等待任务,但数据仍然不可用,则您的线程将向上调用其调用堆栈,以查看您的调用方是否有事情要做,直到您的调用方等待。再次向上调用堆栈以查看是否有工作要做,等等。
async Task<MyResult> GetMyData(...)
{
Task<SomeData> taskGetData = GetDataFromCloud(...);
// while the data is being fetched, we can continue working
DoSomethingElse();
// now we need the data:
SomeData fetchedData = await taskGetData;
MyResult result = ProcessFetchedData(fetchedData);
return result;
}
帮助我理解异步等待的是这次对Eric Lippert的采访。在中间的某个地方搜索异步等待。
对我有帮助的是这篇由一直乐于助人的斯蒂芬·克利里(Stephen Cleary)撰写的文章。