Anonymous methods was recently added to Delphi (Delphi 10.3), but for those that haven't bothered learning them, here is a quick overview.


In traditional Object Pascal you have to declare everything, like this:


function Calculate(A, B: integer): integer;

begin

 result := A div B;

end;


Traditional functions can also be expanded with "sub procedures", which you define as a part of their header section, like this:


function Calculate(A, B: integer): integer;


 function GetFraction: boolean;

 begin

   result := frac( a / b) > 0;

 end;


begin

 result := A div B;

 if GetFraction then

   inc(result);

end;


Anonymous procedures (or functions) work much the same as sub-procedures, but with two major distinctions:


  1. They support re-entry
  2. They are defined in-place


Re-entry is a big deal because the JavaScript runtime environment will make sure that referenced variables and data in the code-block is still available on re-entry. This is not the same as how Delphi or C++Builder deals with things, because JavaScript will retain the entire data context, not just the variables within the anonymous procedure.

The reasoning behind this, is that anonymous methods are used as event-handlers and callback-handlers for asynchronous calls. JavaScript supports the notion of passing methods as parameters (a.k.a "callbacks"), solving and making asynchronous programming sane and manageable. If you had to declare a separate procedure or function for every possible asynchronous call you needed to do - it would be practically impossible to write and work with file-systems, sockets and anything that might not respond immediately.


For example, take something simple as reading from a file. In an asynchronous environment this is a non-blocking operation, which means you have to provide a procedure that will handle the data once it's available.

You are basically telling the system "Do this and then execute this procedure when its done":


procedure TForm1.LoadFile(TagValue: Variant; Filename: string; CB: TFileLoadCB);

begin

 FFileSystem.ReadFile(TagValue, Filename, procedure (TagValue: variant; Filename: string; Data: TStream; Error: Exception)

 begin

   // Did ReadFile() fail?

   if Error <> nil then

   begin

     // Deliver the exception back to our invoker

     // Only throw the exception if no re-entry is provided

     if assigned(CB) then

       CB(TagValue, FileName, nil, Error)

     else

       raise Error;

     exit;

   end;


   WriteLnF("File %s loaded, %d bytes available", [Filename, Data.Size]);


   //Data is ready, deliver to invoker

   if assigned(CB) then

     CB(TagValue, FileName, Data, nil);

 end);

end;


In the above code we invoke the ReadFile() method on a file-system class. We pass along a TagValue (see section on the TagValue paradigm) as the first parameter, the filename, and finally an anonymous procedure. That procedure will be invoked by ReadFile() whenever the data stream is available, or if an error occurred. If an error did occur, the exception is not raised immediately - but instead delivered to our procedure. That way, we can chose to either throw or mitigate the error. In our case above, we just pass it back to the initial invoker that called TForm1.LoadFile().


The point of the code above is that, we don't know when ReadFile() will return. It can take 1 millisecond, 1 second - or 30 minutes! (obviously it rarely takes that long, but you get the idea). Asynchronous programming really is non-blocking, and you have to write code that goes with the flow. Once you get the hang of it, you will knock out code that thrives.


Remember I mentioned that JavaScript will retain the code-block context until re-entry? In the above code, the Filename parameter in LoadFile() will still be readable and valid. As would variables explicitly defined in the var section of the procedure. Inline variables however, could be eaten by the garbage collector.


This is where the TagValue paradigm comes in, namely that you can send a value you want preserved into a process, and safely get it back on re-entry. You can read more about the TagValue concept here.

It is also worth noting that all Widget (visual controls) constructors use anonymous methods, achieving optimal performance as a result.