How to avoid a waterfall callback?

One of the reasons why I'm generally afraid to write Javascript for something other than relatively trivial bits of functionality is because I never found a decent approach to avoid a callback waterfall when one thing really depends on other. Is there such an approach?

I am currently working on a titanium application and stumble upon this real-world scenario:

I have a set of features for which I need to calculate the distance from the user's current location. This entails obtaining the user's current position (which should happen only once), and moving around the objects, getting a position for each of them and calculating the distance. The APIs that retrieve locations (long / lat) are asynchronous, so the “simple” approach is as follows (pseudocode follows):

foreach facility {
  API.getCurrentLocation( function( location ) { // async, takes a callback fxn arg
    var here = location.coordinates;

    API.getFacilityLocation( function( e ) { // async, takes a callback fxn arg
      var there    = e. coordinates;
      var distance = API.calculateFrom( here, there );
    });
  });
}

Since all this is in a cycle, every time I calculate my current position - more work than I really need. I have not yet been able to reorganize it so that I only get the current location once and still have this location to calculate the distance.

, , , - , , , .

?

.

+5
4

: , .

:

function recieveCurrentLocation(location) { // async, takes a callback fxn arg
    var here = location.coordinates;

    // Have to define this callback in the inner scope so it can close
    // over the 'here' value
    function recieveFacilityLocation(e) {
        var there    = e. coordinates;
        var distance = API.calculateFrom( here, there );
    }

    foreach facility {
        API.getFacilityLocation(recieveFacilityLocation);
    }
}

API.getCurrentLocation(recieveCurrentLocation);
+3

. , , . , - : , , , , ..

, . :)

+3

. - ( "watterfall" ), async, , .

, , .

f1(arg1, function(){
    arg2 = g(arg1);
    f2(function(){
        ...use arg2
    });
});

var arg2;
f1(arg1, afterf1);

function afterf1(){
    arg2 = g(arg1);
    f2(afterf2);
}

function afterf2(){
    ...use arg2;
}

, , , , ( - , , , ).

, , .

var x = f();

, x, .

f(function(x){
   ...use x here
});

, - x.

"" , , , .

var got_result = false;
var result = null;
var waiting_for_result = [];

function register_callback(f){
    if(got_result){
        f(result);
    }else{
        waiting_for_result.push(f);
    }
}

var real_callback = function(x){
    got_result = true;
    result = x;
    for(var i=0; i< waiting_for_result.length; i++){
        waiting_for_result[i](result);
    }
}

//

API.getCurrentLocation(real_callback);
foreach facility {
    register_callback(function(location){
        ...stuff
    })

, PITA, Promise, . , - " " .

, Dojo

var location_promise = API.GetLocationPromise();
foreach facility {
    location_promise.then(function(location){
        ...use location
    });
}
+1

?

var here ;    
API.getCurrentLocation( function( location ) {here = location.coordinates;})

    foreach facility {

        API.getFacilityLocation( function( e ) { // async, takes a callback fxn arg
          var there    = e. coordinates;
          var distance = API.calculateFrom( here, there );
        });
    }

0

All Articles