Jump to content
DavidOne

Retrieving data from REST async call

Recommended Posts

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

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

  • Thanks 1

Share this post


Link to post
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 by DavidOne

Share this post


Link to post
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

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

×