views:

63

answers:

3

I am writing some Javascript/jQuery code that requires me to make lots of requests to various APIs. I have come across this problem many times and this is just a basic example of one instance of it.

The code makes an asynchronous request to a server and then if a condition with the returned data is not met, the code makes another request. The second request has a callback that contains much of the same logic as its parent. I know that I am avoiding repetition by calling the returnFunction in each callback but is there a way to avoid this altogether?

var container = [];

$.getJSON("http://www.url.com/api/?callback=?", 
{ data: "mydata" }, 
function(data) {
    if(!data.length) {
        // No results
    } else {

        // Put the results into an array
        $(data).each(function(k, v){
            container.push(v.some_data);
        });

        // If the query did not return enough results
        if(data.length < limit) {
            var number_missing = limit - data.length;

            // Get some more results and append to the array
            myFunctionToGetSomethingElse(number_missing, function(response){

                // Add these results to the array
                $(response).each(function(k, v){
                    container.push(v.some_data);
                });

                // Do something with this data
                returnFunction(response);
            });

        } else {
            // Do something with the result
            returnFunction(data);
        }
    }
});

How would you recommend I avoid the repetition of the code inside the callbacks? Is this the only possible way?

+1  A: 

There is a library delivering "stratified javascript". Oddly enough, it wraps asynchronous calls into a sequential paradigm. Back to square one :)

Take a look at oni labs.

xtofl
Accepted your answer because you were first. tomg's answer was very helpful though! Seems like a pretty cool tool.
betamax
A: 

I don't have an answer to your question - how to eliminate the duplication - but one way to make this a bit cleaner would be to flatten it out with early returns.

$.getJSON("http://www.url.com/api/?callback=?", 
{ data: "mydata" }, 
function(data) {
    if (!data.length) {
        // No results
        return;
    }  // no "else" needed, because we left the function

    // Put the results into an array
    $(data).each(function(k, v){
        container.push(v.some_data);
    });

    // if the query DID return enough results, return them and exit
    if (data.length >= limit {
        // Do something with the result
        returnFunction(data);
        return;
    } // again, no "else" needed; we don't need to deeply nest the code

    // The query did not return enough results
    var number_missing = limit - data.length;

    // Get some more results and append to the array
    myFunctionToGetSomethingElse(number_missing, function(response){

        // Add these results to the array
        $(response).each(function(k, v){
            container.push(v.some_data);
        });

        // Do something with this data
        returnFunction(response);
        });
    }
});

I would think you could wrap the array-loading loop into its own function; I can't see why that would be a problem, but I don't know javascript well enough to say so with absolute confidence.

Carl Manaster
+1  A: 
// <script src="http://code.onilabs.com/0.9.1/oni-apollo.js"&gt;&lt;/script&gt;
// <script type="text/sjs">

// install stratified versions of jquery functions; see below
require('jquery-binding').install();

// Note the extra '$' in $getJSON. This is the 'stratified' version of
// getJSON. Instead of taking a callback it blocks until the result is
// available. The browser will stay responsive during that time.

var data = $.$getJSON("http://www.url.com/api/?callback=?", {data: "mydata"});
if (!data.length) {
  // no results
} else {

  $(data).each(function(k, v){
    container.push(v.some_data);
  });

  if (data.length < limit) {
    var number_missing = limit - data.length;
    // assuming your function 'myFunctionToGetSomethingElse' uses
    // something like $.$getJSON inside, again you don't need a
    // callback
    $(myFunctionToGetSomethingElse(number_missing)).each(
      function(k, v){
        container.push(v.some_data);
      });
    }
}

// console.log(container);
// do something with the result. E.g. call returnFunction() or put the
// code from returnFunction here directly

You can get more information on http://onilabs.com/apollo

tomg