Asynchronous Code In JavaScript
Written by Ian Elliot   
Monday, 15 May 2017
Article Index
Asynchronous Code In JavaScript
Asynchronous Flow Of Control and Closure
Custom Asynchronous Functions
Summary

Events are central to the programming in JavaScript and most programmers master their use early on. Asynchronous programming is just another aspect of an event driven environment, but this is much more difficult to master. Let's find out what the relationship is and why it is so difficult. 

Just jQuery
Events, Async & AJAX

Is now available as a print book: Amazon 

jquery2cover

 

Contents

  1. Events, Async & Ajax (Book Only)
  2. Reinventing Events
  3. Working With Events
  4. Asynchronous Code
  5. Consuming Promises
  6. Using Promises 
  7. WebWorkers
  8. Ajax the Basics - get
  9. Ajax the Basics -  post
  10. Ajax - Advanced Ajax To The Server
  11. Ajax - Advanced Ajax To The Client
  12. Ajax - Advanced Ajax Transports And JSONP
  13. Ajax - Advanced Ajax The jsXHR Object
  14. Ajax - Advanced Ajax Character Coding And Encoding 

Also Available:

buy from Amazon

smallcoverjQuery

 

Advanced Attributes

 

A JavaScript program is a collection of event handlers waiting for something to happen.

When something does happen the event causes the attached event handlers to run. Once we are into the event handler this is just standard programming, nothing new here, but for a single threaded language like JavaScript this isn't quite true. As we have already discussed in the previous chapter the thread of execution that does the work in an event handler is also the thread that responds to the user interface UI. If an event handler takes too long then the UI freezes.

So how can an event handler that cannot avoid doing something that takes a long time keep the UI responsive?

First notice that an event handler can initiate processes that take an incredibly long time compared to the speed that a JavaScript program runs. For example if you use Ajax to download a file or do anything connected with the network then the time to complete the action is likely to be seconds and perhaps minutes. If the event handler waits for the task to complete then the UI freezes for a very unacceptable time.

Using a single threaded event based UI forces us to introduce the idea of asynchronous tasks or functions. 

Ordinarily a function will return when it has finished running its code. This is generally called a synchronous or blocking function because it blocks the UI thread until it is complete. To avoid blocking the UI thread we need to invent non-blocking functions that return as soon as possible and nearly always before they have completed their task.

A non-blocking function sets a process in motion and then returns to the calling program. 

This allows the calling program to do whatever it has to and then return to allow the thread to process the UI. 

This all seems very nice but you can see that there is a problem and it is a big problem. 

The non-blocking function returns before it has completed and this means that the event handler returns before it has the result of the call, so the job isn't done. We need to activate some code to process the results of the non-blocking function.

How are we to do this?

The standard solution is to provide a callback function to the non-blocking function. A callback is a function that the non-blocking process calls when it has really finished its task. 

As functions are first class objects in JavaScript using callbacks is relatively easy - you don't have to invent extra ideas such as delegates (C#), anonymous classes (Java) and lambdas (almost all languages) - to pass a function as a parameter. In fact you almost certainly have been using callbacks for a long time perhaps with out realizing their connection to events.

A callback is like an event handler which fires when the non-blocking function complete its task. 

You can even implement a callback so that it looks exactly like an event. 

For example, consider the Ajax get function, more about which in later chapters, which simply downloads the specified file from the server:

$.get("test.txt",function(data){ alert(data); });

In this case the callback is an anonymous function that simply displays the data stored in the file. Notice that if the get was a blocking function the code would read:

$.get("test.txt");
alert(data);

and there would be no need for a function of any sort.

It is easy, but a little messy, to convert this to an event on an object. To do this we need an object with a new get function that will download the specified url and then raise the loaded event:

var ajax=$({});

ajax.get=function(url){
 $.get(url,function(data){trigger("loaded",data);}); };

Notice that now the callback function simply fires the loaded event with data as the custom data object. 

To use this new get method on the new ajax object all we have to do is register an event handler and call the method:

ajax.on("loaded",function(data){alert(data);});
ajax.get("test.txt");

Now when the file has downloaded the loaded event is fired on the ajax object and the event handler is called. 

You can see that it is a matter of taste whether you use a callback or an event for the completion of a non-blocking function. In practice the DOM makes use of events and JavaScript makes use of callbacks, but this isn't a hard and fast rule. If an object is involved, events are also more natural.

The Problem With Asynchronous

Callbacks may be like event handlers, but they way that they occur in code makes them a very different proposition. Event handlers are in the main easy to write and cause few problems, but callbacks have some serious problems. The reason for this difference is that event handlers are set up by code that has no interest in their results. When you set a button's click handler the code that sets it generally doesn't want to interact with the click handler later in time when a click occurs. It's a set and forget operation. In other words, event handlers are usually fairly closed pieces of code that don't depend on anything else.

Callbacks, on the other hand, generally do something that the code that sets them up cares about. Generally you want to download a file and process the data it contains. In an ideal world the download would complete and then the code would continue on its way to process the file. When you have to use a callback this isn't the way it happens - the process is initiated and the code that started it comes to an end; only later does the callback activate and the process is completed elsewhere.

This is more difficult. 

You will hear lots of explanations of the problem of asynchronous code along the lines of "callback hell", and the "callback pyramid of doom". These are problems but they arise from taking particular approaches to asynchronous programming. 

The first problem is that raw asynchronous programming distorts the intended flow of control.

In a synchronous program you might write

loadA();
loadB();
loadC();

and you can expect A to load before B which loads before C.

As soon as you convert these to async operations you can't be sure what order things are done in unless you adopt the callback cascade:

loadA(loadB(loadC()));

where each function accepts a callback as its parameter.

The callback approach to async turns sequential default flow of control into nested function calls. But keep in mind that the callback approach is just one of many. Because it is so widely used, there is a tendency to think that a callback is the only way to deal with asynchronous code. 



Last Updated ( Thursday, 05 May 2022 )