2014-12-03 110 views
8

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

你推荐这样做的方式?我看到Embarcadero为线程引入了新的任务和特性功能,但我仍然不确定我们是否可以使用该功能以及如何使用它。另外,我看到有使用此函数异步执行TRestRequest,功能:

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

,但我无法找到如何使用此方法和它做什么的任何文件。

任何输入将不胜感激。

+0

@JerryDodge不是异步暗示它不会阻止?最终,异步操作将终止,并且应用程序可以对结果进行一些操作。 – mjn 2014-12-03 19:31:17

+0

@mjn这个按钮点击我们将调用这7个Web服务的地方实际上会打开一个多视图,在我们的例子中将包含一堆过滤组合框,这些组合框将通过从Web服务获取数据来填充。我的想法是让用户能够实际取消该过滤,然后通过触摸多视图之外的区域返回,这将关闭多视图并且不应用任何过滤器。多个并行请求的想法是,如果我们并行发送7个请求,而不是一次发送一个请求,那么我们认为这7个组合框将更快地填充。 – Emulic 2014-12-03 21:36:45

+0

@JerryDodge您应该在后台执行** all ** long(er)正在运行的任务,以防止UI线程被阻塞。这不仅是为用户,而且操作系统正在寻找没有响应的应用程序。 – 2014-12-03 23:29:41

回答

2

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

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

ExecuteAsync方法在REST.Client中定义。 (我有德尔福XE-10.1柏林,所以它有一个额外的参数,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处理。
默认情况下,ACompletionHandler在由ExecuteAsync创建的新线程上运行。如果您希望它在主线程上运行,则应将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; 

如果你想火并联多个请求,则需要使用这种模式几次。不幸的是,我们没有直接访问我们的完成处理程序中的TRESTRequest实例或TRESTResponse实例,因为这意味着我们必须自己做记账。换句话说,程序员要确保完成处理程序处理正确的响应对象。