Alex Rivera | Logout

Javascript - synchronizing after asynchronous calls

Asked 2009-05-13T03:23:14.243
20

I have a Javascript object that requires 2 calls out to an external server to build its contents and do anything meaningful. The object is built such that instantiating an instance of it will automatically make these 2 calls. The 2 calls share a common callback function that operates on the returned data and then calls another method. The problem is that the next method should not be called until both methods return. Here is the code as I have implemented it currently:

foo.bar.Object = function() {
this.currentCallbacks = 0;
this.expectedCallbacks = 2;

this.function1 = function() {
    // do stuff
    var me = this;
    foo.bar.sendRequest(new RequestObject, function(resp) {
        me.commonCallback(resp);
        });
};

this.function2 = function() {
    // do stuff
    var me = this;
    foo.bar.sendRequest(new RequestObject, function(resp) {
        me.commonCallback(resp);
        });
};

this.commonCallback = function(resp) {
    this.currentCallbacks++;
    // do stuff
    if (this.currentCallbacks == this.expectedCallbacks) {
        // call new method
    }
};

this.function1();
this.function2();
}

As you can see, I am forcing the object to continue after both calls have returned using a simple counter to validate they have both returned. This works but seems like a really poor implementation. I have only worked with Javascript for a few weeks now and am wondering if there is a better method for doing the same thing that I have yet to stumble upon.

Thanks for any and all help.

Edit
Report

2 Answers

8

I can add that Underscore.js has a nice little helper for this:

Creates a version of the function that will only be run after first being called count times. Useful for grouping asynchronous responses, where you want to be sure that all the async calls have finished, before proceeding.

_.after(count, function)

The code for _after (as-of version 1.5.0):

_.after = function(times, func) {
  return function() {
    if (--times < 1) {
      return func.apply(this, arguments);
    }
  };
};

The license info (as-of version 1.5.0)

answered 2011-12-20T10:44:29.220
2

That's some good stuff Mr. Kyle.

To put it a bit simpler, I usually use a Start and a Done function.
-The Start function takes a list of functions that will be executed.
-The Done function gets called by the callbacks of your functions that you passed to the start method.
-Additionally, you can pass a function, or list of functions to the done method that will be executed when the last callback completes.

The declarations look like this.

var PendingRequests = 0;
function Start(Requests) {
    PendingRequests = Requests.length;
    for (var i = 0; i < Requests.length; i++)
        Requests[i]();
};
//Called when async responses complete. 
function Done(CompletedEvents) {
PendingRequests--;
    if (PendingRequests == 0) {
        for (var i = 0; i < CompletedEvents.length; i++)
            CompletedEvents[i]();
    }
}

Here's a simple example using the google maps api.

//Variables
var originAddress = "*Some address/zip code here*"; //Location A
var formattedAddress; //Formatted address of Location B
var distance; //Distance between A and B
var location; //Location B

//This is the start function above. Passing an array of two functions defined below.
Start(new Array(GetPlaceDetails, GetDistances));


//This function makes a request to get detailed information on a place. 
//Then callsback with the **GetPlaceDetailsComplete** function
function GetPlaceDetails() {
    var request = {
        reference: location.reference //Google maps reference id
    };
    var PlacesService = new google.maps.places.PlacesService(Map);
    PlacesService.getDetails(request, GetPlaceDetailsComplete);
}

function GetPlaceDetailsComplete(place, status) {
    if (status == google.maps.places.PlacesServiceStatus.OK) {
        formattedAddress = place.formatted_address;
        Done(new Array(PrintDetails));
    }
}


function GetDistances() {
    distService = new google.
answered 2012-04-12T17:03:01.290

Your Answer