bravesofts 30 Posted Friday at 06:18 AM Delphi Async Helpers with TFuture<T>: 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.pas: A generic async helper unit Main.View.pas: A VCL form demonstrating good and bad usage of futures and background tasks.  đ§ Helper Static Class: TSimpleFuture<T> A simple abstraction over TTask.Future<T> and TTask.Run to make async coding easier. type TConstProc<T> = reference to procedure (const Arg1: T); 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;  đź Demo Form: `Main.View.pas`  The form includes buttons that demonstrate the following patterns:  â Correct Usage (Non-Blocking):   `đŞÂ BtnStartWithHelper`: TSimpleFuture<string>.RunFuture(function: string begin    Sleep(2000);    Result := TimeToStr(Now) + ' â ت٠اŮŘŞŮŮŮذ';  end, LogReply); > â Background-safe > â Memory-safe > â Beginner-friendly  â ď¸ Incorrect Usage (`BtnWrongUse`): 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`): LFuture := TTask.Future<Integer>(...); TTask.Run( procedure begin var LValue := LFuture.Value; // Block or wait inside background thread. TThread.Queue(nil, procedure begin LogReply('Result: ' + LValue.ToString); // update UI on main thread 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`): var LResult: string; LFuture := TTask.Run(...); TTask.Run(procedure begin LFuture.Wait; // Call wait inside background thread like IFuture<T>.Value does .. TThread.Queue(nil, procedure begin LogReply(LResult); LFuture := nil; // release `IFuture<T>` reference to avoid Memory Leak (TCompleteEventWrapper etc of internal thread pool that service the TTask). end); 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: var LFuture := TTask.Future<string>(...); TTask.Run(procedure begin while not (LFuture.Status in [TTaskStatus.Completed, TTaskStatus.Canceled, TTaskStatus.Exception]) do TThread.Sleep(100); // Reduce CPU Usage ..(Check every 100 Millisec). 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 (in my opinion now): â 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 (not mandatory, but my Demo is VCL)  - `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: 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. đ§ if you find any Remarks or Suggestions , please Notify me bellow.  Thank you for your Time my Friends.  My Ready to use Github Repo here. Share this post Link to post
Dalija Prasnikar 1512 Posted Friday at 06:50 AM The whole point of Delphi Future implementation is to be a blocking call. Now, we can discuss whether this kind of design is useful or not and in which circumstances.  However, your improved Future is mostly useless. Instead of creating a future and then resolving it in additional task (you are involving two threads here) you should just run the code in the task and that is it.  Simply use  TTask.Run( procedure begin var LValue := (...); TThread.Queue(nil, procedure begin LogReply('Result: ' + LValue.ToString); // update UI on main thread that service the TTask end); end); No need to overcomplicate things. 1 Share this post Link to post
bravesofts 30 Posted Friday at 06:54 AM 2 minutes ago, Dalija Prasnikar said: The whole point of Delphi Future implementation is to be a blocking call could you please introduce real work scenario where Future blocking call is usefull. Share this post Link to post
Dave Novo 56 Posted Friday at 07:14 AM (edited) Isn't the point of Future that you create the Future and it immediately begins processing something in a background thread? Usually, it is calculating some sort of value that you don't need for a while.  Then you execute a bunch of other code.  When then code is done, you request the future.Value. This will block until the thread calculation you started originally is completed (or not block at all if the calculation was already done).  If you are not going to block and wait for the value at some point, why use a future? Why not just launch it in a background thread, like Dalija suggested above?  For example, you have a large matrix of values, and you need to calculate the mean of all the columns. But you dont need the mean until the very end of the method. So you can start calculating the mean at the beginning of the method, in a future. The rest of the method can execute, and at the very end you request the mean of all the columns from the future and do what you need with it. Or this can be the result of a complex database query inside the future, or any number of examples.  There is not much difference between a TFuture and launching a TTask at the beginning of the method and then doing TTask.WaitForAllTasks passing in the task you created as far as I can tell. Just some semantic niceties with TFuture if the background task you are running just has a single result. Edited Friday at 07:14 AM by Dave Novo Share this post Link to post
Lars Fosdal 1857 Posted Friday at 07:33 AM Generally speaking I prefer offloading background tasks to a threadpool in-queue. The threadpool messages the main thread when the work is done. The main thread then retrieves the response from the threadpool out-queue.(i.e. mailbox principle). If there are multiple parallell tasks (i.e. a job), the main thread will need to know that, and whenever a thread completes, check if it fullfill the needs of the job and then fire off whatever should happen after the job is complete.  There really is no single best way to deal with background processing - except ensuring that the main thread is not blocked. Share this post Link to post
Dalija Prasnikar 1512 Posted Friday at 08:41 AM 1 hour ago, bravesofts said: could you please introduce real work scenario where Future blocking call is usefull. It can be useful if you have some code running between the point where you start future and point where you retrieve its value:  LFuture := TTask.Future<Integer>(...); // start future task // ... some other code LFuture.Value // use future value If you just start future and then immediately ask for its value then future is completely useless: LFuture := TTask.Future<Integer>(...); // start future task LFuture.Value // use future value  Share this post Link to post
bravesofts 30 Posted Friday at 09:04 AM (edited) 23 minutes ago, Dalija Prasnikar said: It can be useful if you have some code running between the point where you start future and point where you retrieve its value: but how "my some code" that is between knows exactlly how to continue while future still not ready, inorder to avoid app touching in bad time the LFuture.Value // use future value there is no callback in tfuture to use unless monitoring her status with while loop which also freeze the app  Edited Friday at 09:06 AM by bravesofts Share this post Link to post
Lajos Juhåsz 320 Posted Friday at 09:11 AM 5 minutes ago, bravesofts said: here is no callback in tfuture to use unless monitoring her status with while loop which also freeze the app  That is the idea behind future to do something until you definitely need the result of the future. If you want to check if the future is finished you can use the wait method. Share this post Link to post
Lars Fosdal 1857 Posted Friday at 09:12 AM Wait = Block. Events = Asynch w/o block. Share this post Link to post
Lajos Juhåsz 320 Posted Friday at 09:15 AM 1 minute ago, Lars Fosdal said: Wait = Block. Read the documentation:  function Wait(Timeout: Cardinal = INFINITE): Boolean; overload;  You do not have to wait for infinite. Share this post Link to post
Lars Fosdal 1857 Posted Friday at 09:17 AM But you are still blocking, if looping around a wait. Are you going to put a ProcessMessages in that loop? Share this post Link to post
Dalija Prasnikar 1512 Posted Friday at 09:34 AM 26 minutes ago, bravesofts said: but how "my some code" that is between knows exactlly how to continue while future still not ready, inorder to avoid app touching in bad time the You don't and that is the whole purpose of blocking Future, to wait until the result can be used. If you don't want to block, then don't use future at all. Just use task instead.  You are trying to solve the problem that does not exists. Task is async, future is just a blocking task. Trying to make PPL future async is pointless. 1 Share this post Link to post
bravesofts 30 Posted Friday at 09:39 AM đŹ Is Future an accurate name? From a theoretical computer science and functional programming perspective, yes â the term Future (or Promise) has a well-established meaning: A Future<T> represents a value that will be available at some point in the future. But in my critique is fair in the Delphi-specific context, because Delphiâs TFuture<T> is not particularly âintelligentâ in how it informs you when the value is ready. Instead, it expects you to pull (.Value) or manually monitor its .Status.  Quote âThis so-called Future doesnât notify me when itâs done !!â I have to poll it or block for it. So whatâs so âfutureâ about it?â Delphiâs TFuture<T> is more like a deferred computation container than a true reactive future like in JavaScript (Promise), Java (CompletableFuture)  đ§ Final Thought If Delphiâs TFuture<T> had a callback or allowed chaining with .OnComplete(...), then the name âFutureâ would feel more earned. As it stands: đš Itâs a Future â but you must chase it. đ¸ It holds a value â but it wonât tell you when.  The Accurate name for it in my opinion is this "HalfPromise" Fits Perfectly. It promises a result... eventually. But it doesn't promise to tell you when it's ready. No OnComplete, no Then, no observer pattern â you're left guessing or blocking. So it's not a full Promise, just... half of one. what you think friends ? Share this post Link to post
Lajos Juhåsz 320 Posted Friday at 09:44 AM 10 minutes ago, Lars Fosdal said: But you are still blocking, if looping around a wait. Are you going to put a ProcessMessages in that loop?  The question was whether it is possible to check if the future is completed or not. Method Wait can be used to achieve that. (The design of wait could be improved to support non-blocking call, but we can argue if a couple of ms delay is significant or not.)  Nobody mentioned that the future is going to be used in the main thread. Whenever ProcessMessages should be used or process paint messages depends on how long the calculation should take. If it is under a minute or the application should be blocked during the life of the method, it should not be used.  Share this post Link to post
havrlisan 38 Posted Friday at 10:37 AM I understand the reason behind your decision to implement this, but on the other hand, Delphi Promises was published not long ago, which seems very similar to this project. Does it not contain the same functionality, or does it differ in any way? Share this post Link to post
Dalija Prasnikar 1512 Posted Friday at 11:00 AM (edited) 1 hour ago, bravesofts said: A Future<T> represents a value that will be available at some point in the future. And that is still true. This value will be available at some point in the future, when you try accessing the value. If that value is not calculated by then, the call will wait until it is. The future guarantees that you will be able to get the value at that specific point without having to worry about whether it is actually ready or not. If you want to access that value immediately, then you don't need future. If you want to avoid blocking UI, then again you don't need future, just run the code in the task or other kind of background thread.  Also you should not compare Delphi with other languages that support async/await and which will not block the UI.  If you want a future which works with callbacks you should not use PPL future but something else. I have one variant here https://github.com/dalijap/code-delphi-async/tree/master/Part4/24.2 TValue future Edited Friday at 11:04 AM by Dalija Prasnikar 1 Share this post Link to post
bravesofts 30 Posted Friday at 12:25 PM Thank you very much my friends.. especially my dear Dalija Share this post Link to post