Take the 2-minute tour ×
Programmers Stack Exchange is a question and answer site for professional programmers interested in conceptual questions about software development. It's 100% free, no registration required.

Summary: Are there some well-established best-practice patterns that I can follow to keep my code readable in spite of using asynchronous code and callbacks?


I'm using a JavaScript library that does a lot of stuff asynchronously and heavily relies on callbacks. It seems that writing a simple "load A, load B, ..." method becomes quite complicated and hard to follow using this pattern.

Let me give a (contrived) example. Let's say I want to load a bunch of images (asynchronously) from a remote web server. In C#/async, I'd write something like this:

disableStartButton();

foreach (myData in myRepository) {
    var result = await LoadImageAsync("http://my/server/GetImage?" + myData.Id);
    if (result.Success) {
        myData.Image = result.Data;
    } else {
        write("error loading Image " + myData.Id);
        return;
    }
}

write("success");
enableStartButton();

The code layout follows the "flow of events": First, the start button is disabled, then the images are loaded (await ensures that the UI stays responsive) and then the start button is enabled again.

In JavaScript, using callbacks, I came up with this:

disableStartButton();

var count = myRepository.length;

function loadImage(i) {
    if (i >= count) {
        write("success");
        enableStartButton();
        return;
    }

    myData = myRepository[i];
    LoadImageAsync("http://my/server/GetImage?" + myData.Id,
        function(success, data) { 
            if (success) {
                myData.Image = data;
            } else {
                write("error loading image " + myData.Id);
                return;
            }
            loadImage(i+1); 
        }
    );
}

loadImage(0);

I think the drawbacks are obvious: I had to rework the loop into a recursive call, the code that's supposed to be executed in the end is somewhere in the middle of the function, the code starting the download (loadImage(0)) is at the very bottom, and it's generally much harder to read and follow. It's ugly and I don't like it.

I'm sure that I'm not the first one to encounter this problem, so my question is: Are there some well-established best-practice patterns that I can follow to keep my code readable in spite of using asynchronous code and callbacks?

share|improve this question
    
Is there a particular reason your "async" calls have to be done sequentially? Is this a simplified version of some other code? –  Izkata Jun 5 '12 at 20:14
    
@Izkata: The reason is just that I wanted to be nice to the remote server (= not bombard it with hundreds of simultaneous requests). It's not a requirement set in stone. Yes, it's a simplified version of code, LoadImageAsync is in fact a call to Ext.Ajax.request of Sencha Touch. –  Heinzi Jun 5 '12 at 22:10
1  
Most browsers won't allow you to hammer the server anyway - they just queue up the requests and start the next one when one of the previous completes. –  Izkata Jun 5 '12 at 22:38
1  
    
God! lots of bad advice here. No amount of design patterns are going to help you. Look into async.js, async.waterfall is your answer. –  SalmanPK Sep 12 '12 at 15:27

4 Answers 4

up vote 4 down vote accepted

It's highly unlikely that you can achieve with plain js the same level of conciseness and expressiveness in working with callbacks that C# 5 has. The compiler does the work in writing all that boilerplate for you, and until the js runtimes will do that, you will still have to pass an occasional callback here and there.

However, you may not always want to bring callbacks down to the level of simplicity of linear code - throwing functions around doesn't have to be ugly, there's an entire world working with this kind of code, and they keep sane without async and await.

For instance, use higher-order functions (my js may be a bit rusty):

// generic - this is a library function
function iterateAsync(iterator, action, onSuccess, onFailure) {
var item = iterator();
if(item == null) { // exit condition
    onSuccess();
    return;
}
action(item,
    function (success) {
        if(success)
            iterateAsync(iterator, action, onSuccess, onFailure);
        else
            onFailure();
    });
}


// calling code
var currentImage = 0;
var imageCount = 42;

// you know your library function expects an iterator with no params, 
// and an async action with the current item and its continuation as params
iterateAsync(
// this is your iterator
function () {   
    if(currentImage >= imageCount)
        return null;
    return "http://my/server/GetImage?" + (currentImage++);
},

// this is your action - coincidentally, no adaptor for the correct signature is necessary
LoadImageAsync,

// these are your outs
function () { console.log("All OK."); },
function () { console.log("FAILED!"); }
);
share|improve this answer

Took me a bit to decode why you're doing it this way, but I think this might be close to what you want?

function loadImages() {
   var countRemainingToLoad = 0;
   var failures = 0;

   myRepository.each(function (myData) {
      countRemainingToLoad++;

      LoadImageAsync("http://my/server/GetImage?" + myData.Id,
        function(success, data) {
            if (success) {
                myData.Image = data;
            } else {
                write("error loading image " + myData.Id);
                failures++;
            }
            countRemainingToLoad--;
            if (countRemainingToLoad == 0 && failures == 0) {
                enableStartButton();
            }
        }
    );
}

disableStartButton();
loadImages();

It first off as many AJAX requests as it can do simultaneously, and it waits until all of them are completed before enabling the Start button. This will be faster than a sequential wait, and, I think, is much easier to follow.

EDIT: Note that this assumes you have .each() available, and that myRepository is an array. Be careful what loop iteration you use here in place of that, if it isn't available - this one takes advantage of closure properties for the callback. I'm not sure what you have available, though, since LoadImageAsync seems to be part of a specialized library - I see no results in Google.

share|improve this answer
    
+1, I do have .each() available, and, now that you mention it, it's not strictly necessary to do the load sequentially. I'll definitely give your solution a try. (Although I'll accept vski's answer, since it's closer to the original, more general question.) –  Heinzi Jun 5 '12 at 22:12
    
@Heinzi Agreed on how different it is, but (I think) this is also a decent example of how different languages have different ways to handle the same thing. If something feels awkward when translating it to a different language, there's probably an easier way to do it by using a different paradigm. –  Izkata Jun 5 '12 at 22:41

Disclaimer: this answer doesn't answer specifically your problem, it's a generic answer to the question: "Are there some well-established best-practice patterns that I can follow to keep my code readable in spite of using asynchronous code and callbacks?"

From what I know, there is no "well-established" pattern to handle this. However, I've seen two kinds of methods used to avoid the nested callbacks nightmares.

1/ Using named functions instead of anonymous callbacks

    function start() {
        mongo.findById( id, handleDatas );
    }

    function handleDatas( datas ) {
        // Handle the datas returned.
    }

This way, you avoid the nesting by sending the logic of the anonymous function in another function.

2/ Using a flow management library. I like to use Step, but it's just a matter of preference. It's the one LinkedIn uses, by the way.

    Step( {
        function start() {
            // the "this" magically sends to the next function.
            mongo.findById( this );
        },

        function handleDatas( el ) {
            // Handle the datas.
            // Another way to use it is by returning a value,
            // the value will be sent to the next function.
            // However, this is specific to Step, so look at
            // the documentation of the library you choose.
            return value;
        },

        function nextFunction( value ) {
            // Use the returned value from the preceding function
        }
    } );

I use a flow management library when I use a lot of nested callbacks, because it's a lot more readable when there is a lot of code using it.

share|improve this answer

Simply put, JavaScript doesn't have the syntactic sugar of await.
But moving the "end" part into the bottom of the function is easy; and with an inmediately executing anonymous function, we can avoid declaring a reference to it.

disableStartButton();

(function(i, count) {
    var loadImage = arguments.callee;
    myData = myRepository[i];

    LoadImageAsync("http://my/server/GetImage?" + myData.Id,
        function(success, data) { 
            if (!success) {
                write("error loading image " + myData.Id);

            } else {
                myData.Image = data;
                if (i < count) {
                    loadImage(i + 1, count);

                } else {
                    write("success");
                    enableStartButton();
                    return;

                }

            }

        }
    );
})(0, myRepository.length);

You could also pass the "end" part as a success-callback to the function.

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.