Asynchronous Calls (Delphi)

Remoting SDK for Delphi has two versions of asynchronous interfaces:

Async Interface

Every service method defined in a service interface will be split in two parts for this, one method prefixed with Invoke_ that will initiate the request, and one prefixed with Retrieve_ that will complete a previously invoked request and retrieve any results. (Methods that do not contain any out or result parameters will only be represented by an Invoke_ method.)

For example, if your original Delphi service defined a Sum method:

  INewService = interface(IROService)
    function Sum(const A: Integer; const B: Integer): Integer;
  end;

this method would result in two split methods in the async interface:

  INewService_Async = interface(IROAsyncInterface)
    procedure Invoke_Sum(const A: Integer; const B: Integer);
    function Retrieve_Sum: Integer;
  end;

In your application code, you would call Invoke_Sum(5,10), which would initiate the request to the server but return right away without waiting for the result. Your client application would continue performing other tasks, and, at some point in time, call x := RetrieveSum() to obtain the actual result.

Working with Async Interface

In addition to the split invoke/retrieve methods of your service, the async interface also contains a number of helpful properties it inherits from the IROAsyncInterface. The AnswerReceived property will specify if an answer for the previous Invoke call has been received yet.

You can use this to determine whether your code should call the Receive_ method yet. Calling Receive_ when an answer is not yet available will raise an EROAsyncNoAnswerYet exception.

Similarly, the AnswerReceivedEvent property can be used on channels that support active asynchronous calls to get a Win32 event object that will be triggered when an answer comes in.

AsyncEx Interface

This implementation is similar to .NET approach.

Every service method defined in a service interface will be split in three parts for this, two methods prefixed with Begin that will initiate the request, and one prefixed with End that will complete a previously invoked request and retrieve any results.

For example, if your original Delphi service defined a Sum method:

  INewService = interface(IROService)
    function Sum(const A: Integer; const B: Integer): Integer;
  end;

this method would result in three split methods in the async interface:

  INewService_AsyncEx = interface(IROAsyncInterfaceEx)
    function BeginSum(const A: Integer; const B: Integer; const aCallback: TROAsyncCallback; const aUserData: Pointer = nil): IROAsyncRequest; overload;
    function BeginSum(const A: Integer; const B: Integer; const aCallbackMethod: TROAsyncCallbackMethod; const aUserData: Pointer = nil): IROAsyncRequest; overload;
    function EndSum(const aRequest: IROAsyncRequest): Integer;
  end;

In your application code, you would call BeginSum(5,10, callback), which would initiate the request to the server but return right away without waiting for the result. Your client application would continue performing other tasks, and, at some point in time, call x := EndSum(aRequest) to obtain the actual result.

Working with AsyncEx Interface

Simple example:

procedure TMyForm.callback(const aRequest: IROAsyncRequest);
begin
  if not aRequest.Cancelled then
    fsum := fAsyncExService.EndSum(aRequest)
  else 
    raise Exception.Create('BeginSum call was cancelled');
end;

  ...
  fAsyncExService.BeginSum(5, 10, callback);

Under the Hood of Asynchronous Calls

How asynchronous method calls are handled internally largely depends on the channel type.

Some channels, including the Super TCP Channel, the UDP Channel and the Email Channel, provide active support for asynchronous calls. They know how to transmit an async request to the server and to actively handle receiving a response.

Other channels, including the standard HTTP and TCP channels are unaware of asynchronous requests. When executing an async call on one of these channels, the asynchronous behavior will be simulated on the client side by spawning a worker thread that performs the actual (synchronous) call and waits for the response.

This difference is handled internally by the asynchronous proxy, so your application code will not know the difference.