如何处理异步函数中的 return 值

How to handle return values in async function

在处理使用异步休息调用的数据 API 时(我正在使用 RestSharp.Portable),处理 return 值的最佳方法是什么?由于异步函数只能 return 一个 Task 或 Task ...但是调用者无法返回到 return 值 ... API return 怎么会数据返回给调用者?全局属性?

从我目前所读的内容来看,回调函数似乎是与响应数据交互的唯一方式?

以下面的方法为例;以前我没有使用异步 Rest 库并且能够 return 一个值但是在将它转换为使用 RestSharp.Portable 之后,我看不到 return 值的方法:

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;
    }

这里我所能做的就是 return 任务,但是调用者仍然无法获取响应数据,或者我是否遗漏了一些明显的东西?调用者可以订阅在 Task.Completed 等时触发的事件吗?

我对这个异步概念很模糊。有写Portable data API的例子吗?

我认为您确实需要退后一步,阅读有关如何使用 asyncawait 关键字的信息。除其他事项外,您还需要了解编码 async 方法时在幕后发生的一些编译器魔法。

这里是一个很好的起点:Asynchronous Programming with Async and Await

关于你的问题,Return Types and Parameters 部分是这样说的:

You specify Task<TResult> as the return type if the method contains a Return (Visual Basic) or return (C#) statement that specifies an operand of type TResult.

然后给出如下代码示例:

// Signature specifies Task<TResult>
async Task<int> TaskOfTResult_MethodAsync()
{
    int hours;
    // . . .
    // Return statement specifies an integer result.
    return hours;
}

请注意,尽管方法 return 类型为 Task<int>,但 return 语句只是 return 一个 int,而不是 Task<int>.这基本上是因为有一些编译器魔术正在进行,使得这只在 async 方法中合法。

不想深入了解所有细节,您还应该知道 async 方法的调用者通常希望使用 await 关键字来调用,它知道如何处理TaskTask<TResult> return 值并以透明的方式自动为您解包实际预期的 return 值(幕后有更多编译器魔法)。

所以对于上面的例子,这里有一种调用方式:

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 编程风格的文档。一旦您全神贯注于为您完成的编译器魔术,它就会非常整洁。但是,如果您不花时间真正了解其工作原理,很容易迷路。