Как обрабатывать возвращаемые значения в асинхронной функции
При работе с API данных, использующими асинхронные вызовы rest (я использую RestSharp.Portable), как лучше всего обрабатывать возвращаемые значения? Поскольку асинхронная функция может возвращать только задачу или задачу ... но у вызывающего абонента нет возможности вернуться к возвращаемому значению ... как API вернет данные обратно вызывающему объекту? Глобальные свойства?
Из того, что я прочитал до сих пор, следует, что функции обратного вызова являются единственным способом взаимодействия с данными ответа?
Возьмем следующий метод например; ранее я не использовал библиотеку async 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.Завершено и т. д.?
Я очень нечетко представляю себе эту асинхронную концепцию. Есть ли примеры написания переносимого текста API данных?
1 ответ:
Я думаю, что вам действительно нужно сделать шаг назад и прочитать о том, как использовать ключевые слова
async
иawait
. Помимо всего прочего, вам нужно будет понять некоторые из волшебств компилятора, которые происходят за кулисами при кодировании методовasync
.Вот хорошее место для начала: асинхронное программирование с асинхронностью и ожиданием.
Что касается вашего вопроса, то в разделеReturn Types and Parameters говорится следующее:
Вы указываете
Task<TResult>
как возвращает тип, если метод содержит операторReturn
(Visual Basic) илиreturn
(C#), который задает операнд типа 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
. Это очень аккуратно, как только вы обернете свою голову вокруг магии компилятора, которая делается для вас. Но так легко заблудиться, если у вас нет времени, чтобы действительно понять, как это работает.