Sign in Join

Live chat for Free Pascal, Lazarus, Delphi and every Pascal programmer.

Catalog · How the catalog works

Random project

MBen.Delphi-TFurure

Delphi Async Patterns: The Real Purpose of TFuture (Part 1) !!

View on GitHub

Anyone may press Interesting, signed in or not. It helps others find this project.

Member rating

No ratings yet.

Sign in to rate this project.

README

🚀 Future or Trap? Investigating TFuture<T> Behavior in Delphi (Part 1)

Delphi Deep Dive: Step by Step , Understanding the Right Use of TFuture

my Youtube Video:

TFuture-Async

Please feel free to take a look here ( it's very Important !! ):

This demo project provides a clean and safe way to perform background operations in Delphi using TTask and TFuture<T>, with a focus on keeping the UI responsive and preventing memory leaks.

📦 What's Inside

  • API.Helpers: A generic async helper unit
  • Main.View: A VCL form demonstrating good and bad usage of futures and background tasks

🔧 Helper Class: TSimpleFuture<T>

A simple abstraction over TTask.Future<T> and TTask.Run to make async coding easier.

pascal
type
  TSimpleFuture<T> = class
    class procedure Run(const aQuery: TFunc<T>; const aReply: TConstProc<T>); static;
    class procedure RunFuture(const aQuery: TFunc<T>; const aReply: TConstProc<T>); static;
  end;
  • Run: Runs a background task and replies to the main thread
  • RunFuture: Uses TFuture<T>.Value safely from a background thread

🖼 Demo Form: Main.View

The form includes buttons that demonstrate the following patterns:

✅ Correct Usage (Non-Blocking)

BtnStartWithHelper

pascal
TSimpleFuture<string>.RunFuture(
  function: string
  begin
    Sleep(2000);
    Result := '✅ Executed at this time:' + TimeToStr(Now);
  end,
  LogReply
);

✅ Background-safe
✅ Memory-safe
✅ Beginner-friendly


⚠️ Incorrect Usage

BtnWrongUse

pascal
var LFuture := TTask.Future<string>(...);
LogReply(LFuture.Value); // ❌ Blocks the main thread!

❌ This freezes the UI and defeats the purpose of async programming.


✅ Safe Manual Usage

BtnWaitOutsideMainThread

pascal
LFuture := TTask.Future<Integer>(...);

TTask.Run(
  procedure
  begin
    var LValue := LFuture.Value;
    TThread.Queue(nil, procedure begin
      LogReply('Result: ' + LValue.ToString);
      LFuture := nil; // release `IFuture<T>` reference to avoid Memory Leak (TCompleteEventWrapper etc of internal thread pool that service the TTask).
    end);
  end);

✅ Keeps UI free
✅ Releases LFuture to prevent leaks


🧪 Simulating IFuture<T> with ITask

BtnSimulateIFuture

pascal
var
  LFuture: ITask;
  LResult: string;
begin
  LFuture:= TTask.Run(
    procedure
    begin
      Sleep(3000);
      LResult := '✅ Executed at this time:' + TimeToStr(Now);
    end);

  LFuture.Wait(); // Simulate LFutureValue.Value ..
  LogReply(LResult);
end;

🧠 A useful trick for simulating Future.Value behavior without using TFuture<T>


🔁 Future Monitoring Pattern

BtnMonitorSolution

A more advanced way to ensure task completion:

pascal
var LFuture := TTask.Future<string>(...);

  TTask.Run(procedure begin
    while not (LFuture.Status in
      [TTaskStatus.Completed, TTaskStatus.Canceled, TTaskStatus.Exception]) do
      TThread.Sleep(100);

    TThread.Queue(nil, procedure begin
      if LFuture.Status = TTaskStatus.Completed then
        LogReply(LFuture.Value) else
        LogReply('Future Failled or Canceled !!');
        LFuture := nil; // release `IFuture<T>` reference to avoid Memory Leak (TCompleteEventWrapper etc of internal thread pool that service the TTask).
    end);
  end);

🧼 Best Practices

✅ Do

  • Use TThread.Queue to update UI
  • Use TFuture.Value only from background threads
  • Set LFuture := nil to release memory

❌ Don’t

  • Call .Value on the main thread
  • Forget to release IFuture<T> reference to avoid Memory Leak (TCompleteEventWrapper etc of internal thread pool that service the TTask).
  • Update UI directly from background threads

🧰 Requirements

  • Delphi XE7+
  • VCL application (Windows only)
  • System.Threading unit

📜 License

Free to use and modify in any personal or commercial project.


🧠 Design Philosophy: What Future Really Means
In general software design, a Future is an abstraction that represents a promise of a result to be available in the future. It is not intended to be synchronously accessed using .Value, especially not from the main thread.

❌ Misconception: Using .Value Is Asynchronous
The Future is not designed for synchronous use — instead, it should be part of a fully asynchronous programming style, ideally involving a callback mechanism.

Calling .Value is essentially a blocking call, which defeats the purpose of using Future in the first place.

✅ The Core Idea Behind Future
The essence of the Future abstraction is:

🔹 A promise for a future result without blocking the current thread, preferably using a callback to handle the result when it’s ready.

So using .Value is almost equivalent to Task.Wait — not a true asynchronous pattern

⚠️ Using .Value on the Main Thread Is Misleading!
One of the most common pitfalls developers face with IFuture<T> in Delphi is the assumption that it is meant to be accessed using .Value.

In reality, this goes against the very design philosophy of Future in most programming languages.

In Delphi, calling .Value internally does something like this:

pascal
function TFuture<T>.GetValue: T;
begin
  Wait; // ⛔ This blocks the current thread!
  Result := FResult;
end;

So, it's not just about when the computation starts — it’s about how you consume the result in a way that doesn't harm the user experience.

🔄 Summary
.Value = Blocking = like Wait

Future's goal = Non-blocking promise, best used with callbacks

Using .Value in UI = ❌ Breaks the async model, risks freezing UI

Best practice = Use background thread + TThread.Queue for result delivery

🙌 Contributions

Feel free to open an issue or PR if you want to extend this helper for things like:

  • Cancellation
  • Progress reporting
  • Future.ContinueWith(...) chaining

🧠 Bonus Tip

If you're a beginner, stick with TSimpleFuture<T>.RunFuture(...) — it's the safest way to use TFuture without blocking the UI.