DavidOne 0 Posted September 14, 2022 Hello, I am upgrading my Delphi VCL REST client to introduce async loginc in REST call which are now running in the main thread. I read some articles and docs but none of this put me in the right way. Starting from the simplest thing: the login, I amusing the following code: function TMainDataModule.LoadUser(AUserMailAddress: string): TUser; begin Result := nil; try UserRESTRequest.Params.ParameterByName('MailAddress').Value := AUserMailAddress; UserRESTRequest.Execute; if (UserRESTResponse.Status.Success) then Result := TJson.JsonToObject<TUtente>(UserRESTResponse.Content) except on E: Exception do raise Exception.Create('Non è stato possibile contattare il server'); end; end; If I use the TRESTRequest.ExecuteAsync or create a TTask and run the TRESTRequest.Execute inside I cannot assign the Result of my function (cause the function is execute in the main thread, I suppose...). What is the right way to execute the REST call and retrun the value of the call to the main thread? I have the same problem in assigning the result of a REST call data to my TMemTable. Have you got some advice or different approach about how to retrieve data asycronously? Thank you, Davide Share this post Link to post
Dalija Prasnikar 1396 Posted September 14, 2022 What you want to do is impossible in Delphi (without bringing bad coding practice like Application.ProcessMessages in the process) You cannot have function that will return the result of some asynchronous operation. Period. Asynchronous REST request has events that will run when request is successfully or unsuccessfully finished. In those events you can add logic that needs to run as the result of the operation. procedure TMainForm.ButtonClick(Sender: TObject); begin RESTRequest.ExecuteAsync( procedure begin Memo.Lines.Add(RESTResponse.Content); end, True, True, procedure(Error: TObject) begin Memo.Lines.Add(Exception(Error).Message); end); end; Another way, by running request in task or another thread uses similar approach. In such case you would transform your function into a procedure and pass callback (procedure, method or anonymous method) that will run within thread after the request is completed. If you need to run that code in the context of the main thread you can use TThread.Synchronize or TThread.Queue 1 1 Share this post Link to post
DavidOne 0 Posted September 20, 2022 (edited) On 9/14/2022 at 11:57 AM, Dalija Prasnikar said: What you want to do is impossible in Delphi (without bringing bad coding practice like Application.ProcessMessages in the process) You cannot have function that will return the result of some asynchronous operation. Period. Asynchronous REST request has events that will run when request is successfully or unsuccessfully finished. In those events you can add logic that needs to run as the result of the operation. procedure TMainForm.ButtonClick(Sender: TObject); begin RESTRequest.ExecuteAsync( procedure begin Memo.Lines.Add(RESTResponse.Content); end, True, True, procedure(Error: TObject) begin Memo.Lines.Add(Exception(Error).Message); end); end; Another way, by running request in task or another thread uses similar approach. In such case you would transform your function into a procedure and pass callback (procedure, method or anonymous method) that will run within thread after the request is completed. If you need to run that code in the context of the main thread you can use TThread.Synchronize or TThread.Queue Hello Dalija, thank you for your response! In the end I defined an event in the TMainDataModule and an event handler in the login form. I rewrite REST request execution in the following way: UserRESTRequest.ExecuteAsync( procedure begin var User: TUtente := TJson.JsonToObject<TUtente>(UserRESTResponse.Content); Self.OnAfterLoadUser(Self, User); end, True, True, procedure(Error: TObject) begin raise Exception.Create('It was not possible to contact the server'); end); What do you think about this solution? Is it really thread safe and may be a good workaround? Thank you, Davide Edited September 20, 2022 by DavidOne Share this post Link to post
Dalija Prasnikar 1396 Posted September 21, 2022 20 hours ago, DavidOne said: What do you think about this solution? Is it really thread safe and may be a good workaround? This is appropriate solution. Code that runs inside those anonymous methods will run in the context of the main thread (this is defined by first boolean parameter passed to ExecuteAsync - if you pass False there then that code will run in the context of the background thread). Because it runs in the context of the main thread it will be thread-safe the same way the same way your original function is thread-safe if you run it from the main thread. The only thing you need to pay attention to, is that RESTRequest, RESTClient, and RESTResponse used for making asynchronous request can only handle single request at the time in thread-safe manner. That means, you cannot run UserRESTRequest.ExecuteAsync again while the previous request is not yet completed. Once the request completes, you can reuse those REST components to make another request. Share this post Link to post