Icon Asynchronous Calls

Elevate Web Builder supports asynchronous procedure/function calls using the special async keyword. Asynchronous calls allow the developer to queue a procedure/function call in the browser so that it is run as part of the message queue processing for the main UI thread in the browser. Asynchronous calls are available only at runtime, and will cause a component library compilation error if used in any design-time code.

How Asynchronous Calls Are Executed
Because asynchronous calls are added to the message queue for the main UI thread in the browser, they are executed in a first-in, first-out (FIFO) manner. This means that there may be a delay between when the asynchronous call is made and when the call is actually executed. Also, asynchronous calls are emitted by the compiler as Javascript closures. Closures are functions that are dynamically created and capture the entire run-time scope of their parent execution context. Whenever a closure is actually executed, it will do so using the same scope that was present when the closure was created. Closures are ideal for asynchronous calls, because they need to capture the state of all variables and parameters so that they are available when the call is actually executed.

Executing an Asynchronous Call
To make an asynchronous procedure/function call, simply preface the call with the async keyword. For example,

procedure TForm1.Button1Click(Sender: TObject);
begin
   async CreatePanel(0);
end;

will queue up a call to the CreatePanel procedure so that it will run in the next round of message processing in the browser. Because the compiler will emit a closure for this call, the value of any local variables or parameters will be properly captured, even if the parent method that is calling the function/procedure has finished executing.

Mixing Synchronous/Asynchronous Calls
Because the main UI thread in the browser is used for executing all code, any synchronous code will execute before any asynchronous calls that are queued in the message queue. This is important to understand because it determines how you should combine synchronous and asynchronous calls to achieve the desired outcome.

For example, suppose that you want to create a large number of panels in a container, and want to show a progress dialog while the panels are created. To do this, you would normally do something like this:

procedure TForm1.CreatePanels;
var
   I: Integer;
begin
   for I:=1 to 100 do
      TPanel.Create(Self);
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
   ShowProgress('Creating panels...');
   CreatePanels;
   HideProgress;
end;

However, if you were to execute the above code in the browser, you will see that the panels are created, but the progress dialog will never show. This is because the UI updates for the ShowProgress call will not be executed until any other currently-executing code has completed. In this case, this is the CreatePanels and HideProgress calls, so the ShowProgress UI updates will get merged with the HideProgress UI updates, and the progress dialog will never get shown (or will be shown/hidden so fast that you won't see it).

The key to fixing this problem is to allow the UI to update incrementally while we create the panels, and we'll use asynchronous calls to do so:

procedure TForm1.CreatePanel(I: Integer);
begin
   TPanel.Create(Self);
   Inc(I);
   if (I < 100) then
      async CreatePanel(I)
   else
      async HideProgress;
end;

procedure TForm1.Button1Click(Sender: TObject);
begin
   ShowProgress('Creating panels...');
   async CreatePanel(0);
end;

We don't want to use an asynchronous call to ShowProgress because we want it to be executed immediately so that it is the first UI update to occur. However, we do want to queue each CreatePanel call and the HideProgress call because doing so will force them to execute in-order after any UI updates from the ShowProgress call, as well as allow the UI to update during each panel creation.
Image