Have you ever had some code you wanted to run outside of the event handler that causes the code to run? If not, then this blog post isn’t for you. I’m not here to debate why you would want to do that, or if it is a good idea or not. I just know there’ve been times I’ve needed my code to run outside the event handler, or just a bit later.
One use case example: You are calling a slow routine (Network I/O maybe) and don’t want to freeze the UI while you wait for it to execute.
Still with me? Good. What I used to do was drop a TTimer on the form and set the Interval to 1, then enable it to trigger the code to run later. This worked, but it was messy. You had a timer to deal with, and you had to remember to disable it in the event handler, so it didn’t run multiple times. You also could have used a TThread, which may have been a better solution, but still seemed kind of messy, especially if you wanted to update the UI from your code.
Thanks to the new System.Threading library introduced with XE7, I’ve created a simple procedure that makes this a breeze to do. I call the procedure NonBlocking, but you could just as easily call it RunALittleLater, RunOutsideHandler, etc.
<br /> uses System.Threading;</p> <p>procedure NonBlocking(const Proc: TThreadProcedure);<br /> begin<br /> TTask.Create(procedure begin<br /> TThread.Queue(nil, Proc);<br /> end).Start;<br /> end;<br />
All this does is create a task, and then inside the task queue an update back to the main thread to execute the code that is passed to this procedure as an anonymous method. You could easily just write this code inline, but I thought it worthwhile creating a procedure to handle it for me.
Lets look at a normal execution scenario:
<br /> //...<br /> ListBox1.Items.Add('Before Handler');<br /> EventHandler;<br /> ListBox1.Items.Add('After Handler');<br /> //....</p> <p>procedure TForm1.EventHandler;<br /> begin<br /> ListBox1.Items.Add('Inside Handler');<br /> end;<br />
When this is run, our ListBox1 will look like
- Before Handler
- Inside Handler
- After Handler
which is what we would expect. Now when we introduce a call to our new procedure in the EventHandler:
<br /> //...<br /> ListBox1.Items.Add('Before Handler');<br /> EventHandler;<br /> ListBox1.Items.Add('After Handler');<br /> //....</p> <p>procedure TForm1.EventHandler;<br /> begin<br /> ListBox1.Items.Add('Inside Handler 1');<br /> NonBlocking(procedure begin<br /> ListBox1.Items.Add('Outside Handler'); // This will run last<br /> end);<br /> ListBox1.Items.Add('Inside Handler 2');<br /> end;<br />
Our ListBox1 will look like
- Before Handler
- Inside Handler 1
- Inside Handler 2
- After Handler
- Outside Handler
Notice that Outside Handler was the very last line added, even though it is written between Inside Handler 1 and Inside Handler 2. It even occurs after the After Handler line. Also, this works across all platforms: iOS, Android, Windows and OS X.
Everything before and within the call to NonBlocking will execute in order, but the code within NonBlocking will execute after the code that comes after that anonymous method.
If you have a ShowMessage or something else that blocks the UI thread in the event handler, then the code you passed to the NonBlocking procedure will be executed early, which is fine since the UI thread was already blocked.