【问题标题】:How to handle return values in async function如何处理异步函数中的返回值
【发布时间】:2015-07-19 00:05:31
【问题描述】:

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

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

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

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.Completed 等处触发的事件吗?

我对这个异步概念非常模糊。是否有编写可移植数据 API 的示例?

【问题讨论】:

  • 我不明白这个问题。您正在设置任务的结果。任务完成后,该数据将返回给调用者。
  • 并非如此,因为该方法在发送 client.ExecuteAsync 后立即退出。而 tcs.Task 不提供与值交互的直接方式。当我添加 'await' 关键字(强制方法执行在退出之前完成)时,我开始收到特定于 RestSharp.Portable exe 的编译器错误,对我来说并没有多大意义。我将阅读@sstan 建议的文章,看看这些错误是否有意义。我可能在我的实现中遗漏了一些明显的东西。感谢您的回复!

标签: c# asynchronous restsharp


【解决方案1】:

我认为您真的需要退后一步,了解如何使用 asyncawait 关键字。除此之外,您还需要了解在编写async 方法时在幕后发生的一些编译器魔法。

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

对于您的问题,Return Types and Parameters 部分有这样的说法:

如果方法包含指定 TResult 类型操作数的 Return (Visual Basic) 或 return (C#) 语句,则将 Task&lt;TResult&gt; 指定为返回类型。

然后给出以下代码示例:

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

请注意,尽管方法返回类型为 Task&lt;int&gt;return 语句仅返回 int,而不是 Task&lt;int&gt;。这基本上是因为发生了一些编译器魔法,这使得这仅在 async 方法中合法。

不想了解所有细节,您还应该知道async 方法的调用者通常会使用await 关键字来执行此操作,它知道如何处理Task 或@ 987654338@ 返回值并以透明的方式自动为您解开实际的预期返回值(更多的编译器魔法在幕后)。

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

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&lt;T&gt;(request) 而不是 client.ExecuteAsync&lt;T&gt;(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 编程风格的文档。一旦您了解为您完成的编译器魔法,它就会非常简洁。但是,如果您不花时间真正了解它的工作原理,那么很容易迷失方向。

【讨论】:

  • 感谢该文章的链接。我会尝试你的建议,看看我是否能理解这一切。
猜你喜欢
  • 2021-03-04
  • 2020-07-29
  • 1970-01-01
  • 2014-01-07
  • 1970-01-01
  • 1970-01-01
  • 2017-06-13
  • 2018-01-03
  • 2019-06-15
相关资源
最近更新 更多