views:

46

answers:

4

Hi there,

In JavaScript, have a need to retreive data from two API calls and use both to process data for the display.

For example; one call returns tasks with a user id, the other returns a list of user id's and names. Before I display onscreen I want to process the user ids so that the user name is shown against the task data.

(The best I can think of is a flag for each response (set to true when done)which is checked by the other call(s). So that only the last call would proceed to build the page).

Any advice would be hugely appreciated.

Many Thanks, Chris.

+4  A: 

This is a bit of a stub since it doesn't handle failures, but provides a basic flow. Since you don't know which will return first, fire them both off and do something when both have delivered data.

And this assumes that you have some sort of callback that you can customize to call when your api completes a request.

var apiData = {
  data1: null,
  data2: null
};

function callApis() {
  API.call1({ onComplete: function(result) {
    apiData.data1 = result;
    completeApiRequest();
  }});

  API.call2({ onComplete: function(result) {
    apiData.data2 = result;
    completeApiRequest();
  }});
}

function completeApiRequest() {
  if (apiData.data1 && apiData.data2) {
    doStuff();
  }
}

callApis();
Squeegy
Many thanks for this - I already have code to read and respond to response codes from the server (i.e. 200, 500, 404, etc), but I'll prob review the logic where two calls are coming in (it's unlikely that the calls will give different responses, but I guess it's possible - like if a call constuct gets scrambled).
CJR
IMHO, for a better approach you can create an array in the apiData, and do a for-loop to check them all in the completeApiRequest function. That way, you could add more API calls in the future and you will be able to do it with minor changes.
jpabluz
@jpabluz that would be flexible sure, but also less readable. It depends on if this is a common pattern throughout his code. If this is the only case, I think it's simpler to just assign the data the specific named properties that describe what they are expected to contain. But if this happens a lot, making the state holder an array that gets filled up would be a more reusable approach.
Squeegy
A: 

If you're using an AJAX call, I'd put the (second) call that depends on the first set of data, into the callback handler of the first call:

Using jQuery:

jQuery.ajax({
   url: url, /* first call */
   data: myData,
   cache: false,
   success: function(data) {
               /* ... processing code ... */
               jQuery.ajax({
                  url: url, /* second call */
                  data: someMoreData,
                  cache: false,
                  success: function(data) {
                           /* ... processing code ... */
                           }
               });
            }
});

Here's another solution that does it in parallel:

var requestOneCompleted = false;
var requestTwoCompleted = false;

jQuery.ajax({
   url: url, /* first call */
   data: myData,
   cache: false,
   success: function(data) {
               /* ... processing code ... */
               requestOneCompleted = true;
            }
});

jQuery.ajax({
   url: url, /* second call */
   data: someMoreData,
   cache: false,
   success: function(data) {
               /* ... processing code ... */
               requestTwoCompleted = true;
            }
});

setInterval(function() {
   if(requestOneCompleted && requestTwoCompleted) {
      doStuffWithData();
   }
}, 250);

Note I haven't tested this.

Vivin Paliath
This will likely work, and does not require tracking of state, but is slower than it needs to be. If the requests to not depend on each other it will be faster to fetch them in parallel rather than serially like this.
Squeegy
vivin, I believe the order of which call happens isn't essential but that he requires both to have returned data before he can display something.
pr1001
@pr1001 Ah, ok! Then you'd probably need some sort of polling code that checks the value of flags.@Squeegy True, this is slower - the parallel solution with flags will be faster.
Vivin Paliath
Thanks for that I can save the overhead of the flag by checking response is not null, yes the order isn't key, since one of the calls is just a decode of userids (which I'll hang onto until the main data arrives) avoiding additional wait time from serial calls is the main objective.
CJR
@CJ_Reed, I just updated the solution with another one that polls two flags by using a setInterval. Probably not ideal, mainly due to the fact that Javascript is single threaded.
Vivin Paliath
That polling is totally unecesary. Just call a function that checks the status of the requests after each api request returns something. No need to check for state changes until we are informed of a state change.
Squeegy
Ah, I see what you mean. Set a flag and call the function from the handler itself. Much more elegant :)
Vivin Paliath
A: 

I think flags are probably your best. Or, you could do synchronous AJAX calls (then it'd be SJAX of course!) if you want to enforce a sequence of requests. Of course, this will block the remainder of your script from executing until first one and then the second have finished.

pr1001
A: 

I think you're pretty much there already. Maybe use a tiny state machine with three states, noResponse, oneResponse and bothResponses (implied), something along the lines of:

var state = "noResponse";
callOne.onresponse = callTwo.onresponse = function() {
  switch(state) {
  case "noResponse":
    state = "oneResponse";
    break;
  case "oneResponse":
    processBothResponses();
    break;
  }
}

I assume that all the data you need is in your two call objects, and you can examine them and do your cross checking in processBothResponses().

Andreas Jansson
One problem with this, is that the mantainability of the code is going to be a bit complicated, and perhaps in the future you want to add more responses to the call, and that would take you longer if you want to add 10+ responses
jpabluz
That's a valid point. I just like to use state machines for these types of situations, since it allows you to keep all transitions (hence all associated behaviour) in one function. For n call objects I guess you could let an integer tally t model state, and when t == n call the process function.
Andreas Jansson