如何处理异步函数中的返回值
本文关键字:函数 返回值 异步 何处理 处理 | 更新日期: 2023-09-27 18:03:20
在使用异步rest调用(我使用RestSharp.Portable)的数据api上工作时,处理返回值的最佳方法是什么?由于异步函数只能返回一个Task或Task…但是调用者没有办法返回返回值…API如何将数据返回给调用者?全局属性?
从我读到目前为止,似乎回调函数是唯一的方式与响应数据进行交互?
以以下方法为例;以前我没有使用异步Rest库,并且能够返回一个值,但在将其转换为使用RestSharp之后。可移植的,我没有看到返回值的方法:
public async Task<EntityResourceDescriptor> GetEntityDescriptor(string entityType)
{
TaskCompletionSource<EntityResourceDescriptor> tcs = new TaskCompletionSource<EntityResourceDescriptor>();
var req = new RestRequest("/qcbin/rest/domains/{domain}/projects/{project}/customization/entities/{entityType}");
AddDomainAndProject(req);
req.AddParameter("entityType", entityType, ParameterType.UrlSegment);
client.ExecuteAsync<EntityResourceDescriptor>(req, (res) =>
{
if (res.ResponseStatus == ResponseStatus.Error)
{
tcs.TrySetException(res.ErrorException);
}
else
{
tcs.SetResult(res.Data);
}
}
);
return tcs.Task;
}
这里我所能做的就是返回任务,但调用者仍然没有办法得到响应数据,或者我错过了一些明显的东西?调用者是否可以订阅在Task上触发的事件?完成等。
我对这个异步概念很模糊。有没有编写可移植数据api的例子?
我认为你真的需要退后一步,阅读如何使用async
和await
关键字。除此之外,您还需要了解在编写async
方法时幕后发生的一些编译器魔法。
这是一个很好的开始:异步编程与Async和Await。
对于你的问题,返回类型和参数部分是这样说的:
如果方法包含
Return
(Visual Basic)或return
(c#)语句,该语句指定的操作数类型为 result ,则指定Task<TResult>
作为返回类型。
然后给出以下代码示例:
// Signature specifies Task<TResult>
async Task<int> TaskOfTResult_MethodAsync()
{
int hours;
// . . .
// Return statement specifies an integer result.
return hours;
}
请注意,尽管方法返回类型为Task<int>
,但return
语句只是返回int
,而不是Task<int>
。这基本上是因为有一些编译器的魔法使得这个只在async
方法中合法。
不需要了解所有的细节,您还应该知道async
方法的调用者通常期望使用await
关键字来执行此操作,该关键字知道如何处理Task
或Task<TResult>
返回值,并以透明的方式自动为您解开实际期望的返回值(幕后有更多编译器的魔力)。
对于上面的例子,这里有一种调用方法:
int intValue = await TaskOfTResult_MethodAsync(); // Task<int> is automatically unwrapped to an int by the await keyword when the async method completes.
或者,如果您希望启动异步方法,在此期间执行一些其他工作,然后等待异步方法完成,可以这样做:
Task<int> t = TaskOfTResult_MethodAsync();
// perform other work here
int intValue = await t; // wait for TaskOfTResult_MethodAsync to complete before continuing.
希望这能让你对如何从异步方法传递值有一个大致的了解。
对于你的具体例子,我不熟悉RestSharp(从未使用过)。但从我读到的一点,我认为你会想要使用client.ExecuteTaskAsync<T>(request)
而不是client.ExecuteAsync<T>(request, callback)
,以更好地适应async-await
模型。
我认为你的方法应该看起来像这样:
public async Task<EntityResourceDescriptor> GetEntityDescriptor(string entityType)
{
var req = new RestRequest("/qcbin/rest/domains/{domain}/projects/{project}/customization/entities/{entityType}");
AddDomainAndProject(req);
req.AddParameter("entityType", entityType, ParameterType.UrlSegment);
var res = await client.ExecuteTaskAsync<EntityResourceDescriptor>(req);
if (res.ResponseStatus == ResponseStatus.Error)
{
throw new Exception("rethrowing", res.ErrorException);
}
else
{
return res.Data;
}
}
你的调用代码看起来像这样:
EntityResourceDescriptor erd = await GetEntityDescriptor("entityType");
我希望你能设法让这个工作。但是,再次强调,一定要阅读有关async-await
编程风格的文档。一旦你理解了编译器为你所做的一切,你就会发现它非常简洁。但是如果你不花时间去真正理解它是如何工作的,你很容易迷失方向。