我对RestRequest工具比较陌生。我希望有人已经解决了等待异步调用完成的问题。
我有一个程序,它会进行数百个不同的API调用,然后调用处理结果,并将结果传递回调用api执行的过程,然后它可以继续…
唯一的问题,总是做这样的调用没有线程,是软件挂起,直到调用完成。为了尝试补救这个问题,我将RESTRequest.Execute;
更改为RESTRequest.ExecuteAsync();
,但现在我的问题是代码继续运行,而没有等待restrequest的响应。
再次尝试绕过这个问题,我已经尝试了几个解决方案,甚至api.RESTRequest.ExecuteAsync().WaitFor;
(抛出错误thread error: the handler is invalid (6)
)
有没有任何方法可以改变下面的函数作为一个单独的线程运行(只有执行部分是重要的线程运行)...基本上,我只是想在每次调用函数时显示一个动画加载图标,并为我的代码的其余部分直到这个函数完成。
我希望有一个更简单的解决方案,而不是开始在多线程上使用full。
编码
function run_api_command():boolean;
begin
result := false;
RESTResponse.Content.Empty;
RESTAdapter.Dataset:= ds_action;
RESTAdapter.RootElement:= '';
try
RESTRequest.ExecuteAsync;
if(ds_action.Active = false) then ds_action.Active:=true;
if(ds_action.FieldByName('result').AsString<>'Success') then
begin
showmessage(ds_action.FieldByName('result').AsString);
end
else
begin
RESTAdapter.RootElement:= 'data';
result := true;
end;
except
on E: Exception do
begin
if(e.Message = 'REST request failed: Error sending data: (12007) The server name or address could not be resolved') then
begin
if(messagedlg('Could not connect to server. Would you like to retry?', mterror,[mbYes,mbNo],0)=mrYes) then
begin
result := run_api_command();
end;
end
else
begin
showmessage(RESTResponse.Content);
end;
end;
end;
end;
字符串
2条答案
按热度按时间pprl5pva1#
ExecuteAsync()
运行在工作线程(它返回的TRESTExecutionThread
对象)中。但是,ExecuteAsync()
有一个AFreeThread
参数,默认为True
。正如documentation明确指出的那样:当
AFreeThread
参数设置为False时,此方法将向执行线程返回一个reference。注意:如果
AFreeThread
参数设置为True,则该方法返回无效引用。所以在返回的对象指针上调用
WaitFor()
默认情况下会崩溃。即使返回的对象指针在
AFreeThread=True
时有效,调用WaitFor()
仍然会崩溃。当TThread
对象的FreeOnTerminate
属性被设置为True
时,该对象在线程运行结束时释放其底层API句柄(以及从内存中销毁自身),这导致WaitFor()
失败并出现“句柄无效”错误(甚至导致访问违规或类似错误)。TThread.WaitFor()
有一个逻辑错误1,它不能处理TThread.FreeOnTerminate=True
时的情况。(1这个bug是在 Delphi 6中引入的,当时
TThread
被重写以支持Kylix,并且在以后的版本中从未被纠正。如果希望调用方等待REST服务器的响应,请执行以下操作:
Execute()
而不是ExecuteAsync()
,或者至少设置AFreeThread=False
,这样你就可以在线程对象上调用WaitFor()
(或等效的,比如MsgWaitForMultipleObjects()
),然后处理在主UI线程中执行等待的后果。Execute()
,但将整个逻辑移动到自己的工作线程,并根据需要与主UI线程同步。更好的解决方案是根本不等待,这意味着重新设计代码流。继续使用
ExecuteAsync()
,但传递一个完成回调,当请求完成时将调用该回调,并让该回调驱动代码的下一步。不要主动等待ExecuteAsync()
完成,让它通知您。字符串
lawou6xi2#
从雷米的回答来看...
“但现在我的问题是我的代码不等待restrequest的响应就继续”
这就是异步请求的确切目的。你不应该等待,你应该发送请求,然后继续做别的事情。如果您希望捕获结果,还应该提供一个回调过程,但我没有看到您在代码中这样做。
同步调用和异步调用是完全不同的设计体系结构,不能像您希望的那样简单地在它们之间切换。您不能只将Execute更改为ExecuteAsync,而不重新设计其余部分。
Execute
本质上等待调用线程内的响应。但是,ExecuteAsync
本质上会在一个新的线程中产生请求,以便您的代码可以在后台继续执行您的工作。在Async
请求中不涉及等待--这违背了它们的全部目的。在您的情况下,最理想的选择是将一个请求与另一个请求的响应链接起来。意思是,只发送您的第一个请求。一旦收到响应,就从其响应回调中发送下一个请求。
......因为我想将函数创建为一种“全局API调用”函数,以使编码少一点......
那很好。您仍然可以有一个执行所有工作的主过程。在这里,重要的是什么时候打电话。不过,按钮的OnClick事件永远不是执行此类操作的适当位置。该按钮唯一应该做的事情(因为它在主UI线程中)就是发起一个请求。如果您希望UI在此之后能够响应,那么该请求应该以某种方式、形状或形式产生一个新的线程。
不过,无论如何,
WaitFor
都无法满足您的要求。您需要一个响应迅速的UI。WaitFor
将无视这个要求,并锁定应用程序的主线程。在另一个线程中执行此工作的全部目的是显示一个“等待”动画,如果主线程忙碌等待任何事情,则不会显示动画。编辑
我其实想到了另一种可能。仍然只生成您的第一个请求。但是,与其将一个请求与另一个请求的响应“链接”在一起,不如在第一个请求的回调中完成所有其余的工作。由于此时它已经在一个线程中,因此不一定需要为下一个请求生成另一个线程。在第一个请求之后,只需在已经生成的现有线程中执行所需的其余部分。
顺便说一句,这是现代编程世界中一个非常常见的问题...
x1c 0d1x的数据