Javascript Jems - Asynchronous Patterns And Closure |
Written by Ian Elliot | ||||||
Tuesday, 03 June 2014 | ||||||
Page 2 of 2
The Asynchronous ProblemThe problem with closure is that it sounds complicated until you get the idea and once you have got it you can’t really see why anyone in their right mind would bother to implement it. There is a sense in which even if it didn't turn out to be useful you would have to implement it because without it having a Function object that lived longer than variables it is supposed to be able to access would result in some strange behaviour - code that worked in one situation wouldn't in another. However it turns out that there are lots of practical reasons why closure is a good idea but one of the most useful and practical concerns writing asynchronous code. In an asynchronous environment it is tempting to try to impose order simply by making the initiating thread wait for the asynchronous task to complete i.e. to attempt to convert the asynchronous behaviour back into the usual synchronous. For example, suppose you have a function that loads some resource asynchronously. The logic is usually something like
If you try this in JavaScript, and it can be done, the result is that you lock the main UI leaving the user with an unresponsive browser while the resource loads. This is not the way to do it but it is the way we would like to think about it. You also have to include the possibility that the resource will never succeed in loading. Using polling you have to include a test for a timeout and add code to handle the result:
This is how you would like to write the code but can't. It is logical and the text of the program follows the order that things happen in. You can't use it however for the simple reason that step 2 ties up the single thread of execution and this means that the program can't respond to any events and the UI becomes completely unresponsive. Next we'll consider the alternative asynchronous approach.
The Asychronous PatternThe asynchronous approach is known to all JavaScript programmers. All that happens is that you have to provide a callback function which is called when the task completes. An error condition can be signaled either by an error parameter or a separate error callback. The big problem with callbacks is that they mess up the logical flow of control and its associated natural scope. In the synchronous approach the code that follows the call to the asynchronous load now has to be in separate functions and the flow of control is lost at this point.
There is also the problem that now neither onComplete nor onError share variables with the function that started the whole action going and this does often cause problems. If a function starts something loading then it is generally that function that wants to do something with whatever it is that is being loaded. Having to use a callback means that the UI can keep functioning but it also destroys the natural structure of what you are trying to do. In an ideal world the code in onComplete and in onError would be part of the main function. This is where closures come in and it is easier to explain by a small simple example. Picture an exampleTo make things more concrete consider the problem of preloading an image into an Image object:
The main function getPic starts and manages the entire process so you don’t have to go off looking for where the onload event handler is – it’s an inner function to getPic. The Image object created in the first line and the reference to it is local to getPic function. Notice that it’s always a good idea to set the src property after the event handler’s because this starts the load process and some browsers ignore the event handlers if the image is loaded before they are set. So always set event handlers before starting the process that might trigger the event. You might think that no closure would be formed and the displayPic function might not even be called when the image loads. The reason that this does work and a closure is formed is that the Image object created is a DOM object not a JavaScript object. The Image object isn't garbage collected when the getPic function ends and so it has a persistent reference to the displayPic function which hence forms a closure. Notice that with the displayPic function defined within the getPic function all of getPic’s variables are available to the inner function even after the outer getPic function has terminated. For example you can change the alert to:
and the content of the Image object’s property will be retrieved even though the function that the variable “pictureimage” is local to the outer function which finished some time ago. The actual flow of control through the getPic function is such that the tread of execution exits after the line:
There is then a long pause when nothing happens within this code while the picture is loaded and only then does execution pickup at the first line of the displayPic function. You can almost think of this as a “pause while picture loads” pattern. This way of thinking is generally useful in organising asynchronous resource loads. You can extend this way of thinking to include what happens when the resource isn’t available:
Now we have an onerror event handler defined within the outer function and in this case you can think of it as an asynchronous “catch” clause. If you want to restart the picture load say 3 times then you can once again take advantage of the outer local variables which are still available due to the action of closure:
In this case the onerror handler tests and increments the retryCount provided by the outer function. Again there is also the huge benefit that the entire object handling code is grouped together and works together. Such is the advantage of closure. The general principle is:
The only difficulty is in defining what "naturally occur in" means. In most cases it is fairly obvious. It is the outer function where the resource concerned is first loaded. However there are cases where event handlers are easier to write if they have access to data within a particular function and this is their natural home. Of course things get much more complicated if you load more than one image and this is the reason why we need something a little more sophisticated - i.e. Promises, see jQuery, Promises & Deferred.
Related ArticlesJust JavaScript - The Function Object Private Functions In JavaScript A JavaScript TimeInterval object Task.js Asynchronous Tasks In JavaScript
To be informed about new articles on I Programmer, install the I Programmer Toolbar, subscribe to the RSS feed, follow us on, Twitter, Facebook, Google+ or Linkedin, or sign up for our weekly newsletter.
Comments
or email your comment to: comments@i-programmer.info <ASIN:0321683919> <ASIN:0596517742> <ASIN:0321572602> <ASIN:0596806752> <ASIN:1590597273> <ASIN:059680279X> <ASIN:0596805527> |
||||||
Last Updated ( Tuesday, 03 June 2014 ) |