What Is Asynchronous Programming?
Written by Mike James   
Friday, 28 June 2013
Article Index
What Is Asynchronous Programming?
Non-blocking calls

Non-Blocking

The idea of having an event handler return almost at once is the ideal in a single threaded event driven system. This is often refered to as a non-blocking call as opposed to a blocking call which stops the rest of the system moving on because it takes so long. In most cases a blocking call can be converted to a non-blocking call by the simple expedient of returning before its job is done. 

We have already met one big example of converting a blocking to a non-blocking call when the idea of using a new thread to get an event handlers task completed. 

The idea is that if you have a event handler

onEvent(){
 do a lot of work that takes ages;
 return;
}

then you convert it into:

onEvent(){ 
 Create new thread;
 Use thread to run getWorkDone();
 return;
}

This looks very simple but it raises a whole new problem. Usually the way that the applciation behaves depends on the completion of the event handler's task. For example if the event handler was say loading an image over the internet then perhaps the next action that the user takes depends on its completion. The point is that now the application needs to know when the getWorkDone function has completed. 

In other words using a separate thread to complete the work of the event handler has produced a synchronization problem that disrupts the logical flow of the program. 

It also introduces other problems associated with the thread shareing resources and accessing the UI but these are not really central to the problems of asynchronous code. 

Introducing a thread makes things more complex but we have little choice.

Callbacks

The idea of converting a blocking function into a non-blocking one extends to functions which are not themselves event handlers. For example consider the getWorkDone function which takes a long time to complete. You could make it non-blocking by introducing a new thread. 

getWorkDone(){
 create new thread use it to call
 getWorkDone2();
 return:
}

In the case of many system or framework functions you don't even have to imagine that they create a new thread you can regard them as simply returning at once even thought their job hasn't been completed. 

Using the non-blocking form of the getWorkDone function the event handler can be written more simply as:

onEvent(){ 
 getWorkDone();
 return;
}

Notice that now the writer of the event handler doesn't have to worry about creating a new thread or how to run getWorkDone to minimize the slowdown of the UI. 

However this solution, neat though it is, introduces another problem. Usually the event handler would want to do something with the result of getWorkDone. In general when ever a blocking function is converted into a non-blocking function we have the problem of what to do next. The problem is when getWorkDone completes some seconds after onEvent has returned there is no calling function for it to return to. What next?

This idea that a function might not know what to do after it finishes its task can be solved in many ways but they are all variations on the idea of continuation passing - i.e. passing an explicit something to do when the function has finished. 

The simplest and best know of these approaches is to pass a call back function to the non-blocking function that contains the code that is to be executed when the task is complete. 

 onEvent(){ 
 getWorkDone(onCompletion);
 return;
}

The onCompletion function is called when getWorkDone has finished. Notice that onCompletion looks a lot like an event handler and in fact there is very little difference. A call back function is just an event handler that responds to the completion of the non-blocking function.

Of course things are a little more complicated in that this sort of mechanism seems to modify the natural logic of a function. A function that was:

onEvent(){ 
 getWorkDone();
 onCompletion
 return;
}

is distorted into:

onEvent(){ 
 getWorkDone(onCompletion);
 return;
}

and while this example may not look so bad the reality is usually much worse. You might think of a function as loading an image, processing the image and then displaying it but you have to convert into a function that starts the download and an event handler that reacts to the completion of the download. In practice a function could be decomposed into many event handlers or call backs each dealing with the completion of some non-blocking function or other. 

Dealing With Async

Given that asynchronous coding is so prevalent what can we do to make it better. First we need to try to explain what makes it worse.

Writing a collection of event handlers generally isn't a difficult thing to do and with the condition that they all return fast there isn't really any difficulty.

Where things get difficult is when you make use of a lot of non-blocking functions to make sure that event handlers return fast. The problem here is that calling non-blocking functions means passing call backs or completed event handlers depending on how you want to look at it. This is fine when you are only calling one non-blocking function to get a task done but this is usually not the case. 

Typically you need to perform a number of tasks and if the functions are blocking you might write:

 funcA();
 funcB();
 funcC();

To convert this to a set of non-blocking functions would result in something like:

funcA(function(){
                 funcB(function(){
                                 funcC();

                                                                         }

                                                   )

                                    }

            )

 

where it is assumed that each function is passed a callback as its only parameter. That is each function is passed a function which calls the next function in the sequence. This pyramid of doom style callbacks gets much worse in practice. 

For the final example consider a loop:

for(i=0;i<10;i++) funcA(i);

and now convert it into a non-blocking call:

function loop(i){
 if(i>=10) return;
   funcA(i,function(){
     loop(i++);
}(0);

Assuming that funcA takes a callback as its second argument. Yes that's correct an enumeration loop turns into recursion with non-blocking calls. 

There are lots of other problems - handling error conditions, dealing with exceptions and so on. 

All in all asynchronous programming using callbacks in non-blocking functions isn't easy. 

Are there any solutions?

Yes - see Promises, async and await, yield and any number of flow of control libraries that convert the convoluted callback structure into something more familiar. 

We will take a look at these solutions in the next article. 

Related Articles

Task.js Asynchronous Tasks In JavaScript      

jQuery, Promises & Deferred

jQuery Promises, Deferred & WebWorkers

Why await? Why not multithread the UI?       

Async, Await and the UI problem       

 

To be informed about new articles on I Programmer, install the I Programmer Toolbar, subscribe to the RSS feed, follow us on, Twitter, FacebookGoogle+ or Linkedin,  or sign up for our weekly newsletter.

 

blog comments powered by Disqus 

 

 



Last Updated ( Saturday, 29 June 2013 )
 
 

   
RSS feed of all content
I Programmer - full contents
Copyright © 2014 i-programmer.info. All Rights Reserved.
Joomla! is Free Software released under the GNU/GPL License.