【问题标题】:Multiple REST Requests in parallel with FireMonkey多个 REST 请求与 FireMonkey 并行
【发布时间】:2014-12-03 18:46:55
【问题描述】:

我正在 Rad Studio XE7 中构建一个 Firemonkey 应用程序,单击一个按钮,我将需要使用 TRESTRequest 执行多个(大约 7 个)Web 服务调用。每个 Web 服务都将返回 json 对象,然后该对象将填充数据集。 我正在寻找一种同时进行这些调用的方法,并且没有要锁定应用程序的 UI。

您建议采用哪种方式执行此操作?我看到 Embarcadero 为线程引入了新的任务和特性功能,但我仍然不确定我们是否可以使用它以及如何使用它。另外,我看到有一个函数可以异步执行TRESTRequest,使用这个函数:

function TCustomRESTRequest.ExecuteAsync(ACompletionHandler: TCompletionHandler = nil; ASynchronized: boolean = true; AFreeThread: boolean = true): TRESTExecutionThread;

但我找不到有关如何使用此方法及其作用的任何文档。

任何意见将不胜感激。

【问题讨论】:

  • @JerryDodge 不异步暗示它不会阻塞?最终异步操作将终止,应用程序可以对结果执行某些操作。
  • @mjn 在我们将调用这 7 个 Web 服务的地方单击这个按钮实际上会打开一个多视图,在我们的例子中将包含一堆过滤组合框,这些组合框将通过从 Web 服务获取数据来填充。我的想法是让用户能够实际取消此过滤,然后通过触摸多视图之外的区域返回,这将关闭多视图并且不应用任何过滤器。多个并行请求的想法是,我们认为如果我们并行发送 7 个请求而不是一次发送一个请求,这 7 个组合框的填充速度会更快。
  • @JerryDodge 您应该在后台执行 all 长时间运行的任务,以防止 UI 线程阻塞。这不仅适用于用户,而且操作系统也在寻找没有响应的应用程序。
  • @JerryDodge f.i.如果应用程序由于没有响应而被冻结,Android 将弹出一个对话框。所以这样做是必须的。对不起,我无法理解你问为什么的意思。感觉就像在问“你为什么不想开车穿过邻居的花园?” :o)
  • @JerryDodge 这肯定是正确的选择 - 没有任何可接受的良好工作选项

标签: multithreading delphi rest asynchronous


【解决方案1】:

ExecuteAsync(ACompletionHandler: TCompletionHandler = nil; ASynchronized: boolean = true; AFreeThread: boolean = true) 方法确实是要走的路。顾名思义,它是异步的;这意味着您的程序在触发请求后,甚至在触发多个请求后仍保持响应。
但是,从不同的对象实例触发这些不同的请求很重要;如果您从已经执行ExecuteAsyncTRESTRequest 实例触发ExecuteAsync,则新请求将妨碍现有请求。您必须为每个并行调用创建一个单独的 TRESTRequest 实例。

注意它的第一个参数是一个过程;您将您选择的过程作为该参数传递。唯一的要求是程序有正确的签名;在这种情况下,它是一个没有参数的过程。

ExecuteAsync 方法在REST.Client 中定义。 (我有 Delphi XE-10.1。Berlin 所以它有一个额外的参数,ACompletionHandlerWithError - 出错时调用它。但原理保持不变。

让我们看看:

function TCustomRESTRequest.ExecuteAsync(ACompletionHandler: TCompletionHandler = nil; ASynchronized: boolean = true;
  AFreeThread: boolean = true; ACompletionHandlerWithError: TCompletionHandlerWithError = nil): TRESTExecutionThread;
begin
  Result := TRESTExecutionThread.Create(Execute, self, ACompletionHandler, ASynchronized, AFreeThread, ACompletionHandlerWithError);
end;

这里发生的情况是创建了一个执行 REST 请求的新线程。如果响应进来,则由ACompletionHandler处理。
默认情况下,ACompletionHandlerExecuteAsync 创建的新线程上运行。如果你想让它在主线程上运行,你应该将ASynchronized设置为true

但是您如何访问该响应,并使其可供程序的其余部分访问?

FireMonkey 的 TRESTRequest 类有一个属性Response,它引用包含服务器对我们请求的响应的 TRESTResponse 对象。
不幸的是,TRESTRequest 和 TRESTResponse 对象都没有传递给我们的 CompletionHandler!

所以我们需要以某种方式向 CompletionHandler 提供此信息。幸运的是,我们可以使用一个方法作为 CompletionHandler。

假设要处理结果数据的类称为DataOwner。我们的目的是DataOwner 可以访问与我们的 TRESTRequest 实例关联的 TRESTResponse 实例。
最简单的方法是让 TRESTRequest 和/或 TRESTResponse 成为 DataOwner 的成员。

假设您的请求是从名为 MyRESTRequest 的 TRESTRequest 实例触发并由函数 processResponse 处理的,您将使用以下代码:

type TDataOwner = class
   MyData: TSomeDataType;
   procedure GetData();
   procedure FillDataSet();
end;

implementation

procedure DataOwner.GetData();
begin
  // ... initialize MyRESTRequest here...
  MyRESTRequest.ExecuteAsync(FillDataSet);
end;

procedure DataOwner.FillDataSet();
begin
    MyData := processResponse(MyRESTRequest.Response);
end;

如果您想并行触发多个请求,则需要多次使用此模式。不幸的是,我们无法直接访问 Completion Handlers 中的 TRESTRequest 实例或 TRESTResponse 实例,因为这意味着我们必须自己进行簿记。换句话说,由程序员来确保 Completion Handler 处理正确的 Response 对象。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2016-04-26
    • 1970-01-01
    • 2015-06-29
    • 2018-11-08
    • 1970-01-01
    • 2020-03-14
    • 2018-03-26
    • 2017-03-29
    相关资源
    最近更新 更多