如何处理异步函数中的返回值

本文关键字:函数 返回值 异步 何处理 处理 | 更新日期: 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的例子?

如何处理异步函数中的返回值

我认为你真的需要退后一步,阅读如何使用asyncawait关键字。除此之外,您还需要了解在编写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关键字来执行此操作,该关键字知道如何处理TaskTask<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编程风格的文档。一旦你理解了编译器为你所做的一切,你就会发现它非常简洁。但是如果你不花时间去真正理解它是如何工作的,你很容易迷失方向。