среда, 6 января 2010 г.

Asynchronous operations using Deferred object

In this post I want to describe an idea of using Deferred object in asynchronous programming. The idea is not new. It is used widely in AJAX frameworks. The advantage of using such technique is clear and compact code. The technique would be impossible without anonymous functions in Delphi 2009/2010.
So let's start with code example of using deferred object.



The code asynchronously downloads image from the Web and places it in TImage control.
In case of exception error handling function called in context of main application thread - from that context asynchronous download operation were initiated.

The Deferred object is a helper to handle asynchronous operations in common manner.
The object maintains a queue of anonymous functions of 2 kinds. Functions of first kind are called as a result of successful asynchronous operation, second kind are error handlers.

I think it will be handy to expose Deferred as interface with following declaration.


The use scenario of such interface will be:

1. Give reference to IDeferred to some long running asynchronous operation.
2. Add result and error handlers using Add and Err methods of that IDeferred.
3. After long running asynchronous operation ends just assign result to Result property, or if exception is raised - assign that exception object to Result property.

So lets go through first listing according to that scenario

Function QueuedAsyncDownload downloads url into TStream in separate thread. It takes IDeferred as parameter or creates new one if it is omitted and returns that IDeferred.
After download started result handling function is added to IDeferred using .Add method.
Error handler is added next.
After TStream is received it is just set as result of IDeferred.
And that result is used as parameter to first anonymous success function in IDeferred's queue. If result assigned to IDeferred is Exception object then success functions are discarded from IDeferred's queue until error handling function is met, and that Exception is used as parameter for that error handler.
Each of functions (success and error) should also return Result object or nil and the IDeferred queue will be processed again according to that result. If nil is returned - that means no result so no function will be called from IDeferred queue.

Looks like very confusing and too complex design at first glance. And also this is not common way to make things in Delphi. You may ask - Why not just create TThread descendant and to make all the things there with proper synchronization of course?
But when you have to do several asynchronous operations one after other based on result of previous operation code appears not so simple and clear.
I'll try to make several examples of such things in my future posts.