Catherine
Catherine

Reputation: 14010

Pass state to a sub-function in javascript?

I have a set of directions that I'm trying to render in different colors. I request the directions from Google, but I'm not sure how to store the state so that when the routing results are returned I can render each one in the proper color.

So, as an example, I have this object:

{routes: [{start_lat : 0.0, start_lng : 0.0, end_lat : 1.0, end_lng : 1.0, color : "#ff0000"},
          {start_lat : 1.0, start_lng : 1.0, end_lat : 2.0, end_lng : 2.0, color : "#00ff00"}]}

Here is my function:

directionsService = new google.maps.DirectionsService();
for(var i = 0; i < routes.length; i++){
    var dir_request = {
        origin : new google.maps.LatLng(routes[i]['start_lat'], routes[i]['stop_lng']),
        destination : new google.maps.LatLng(routes[i]['end_lat'], routes[i]['end_lng']),
        travelMode : google.maps.TravelMode.WALKING
    };
    directionsService.route(dir_request, function(result, status) {
        if(status == google.maps.DirectionsStatus.OK){
            // render with custom logic depending on which route this is here
        }
    })
}

How can I attach a state (for example, the index in the for loop) to each request so that it's accessible from the result processing function?

Upvotes: 0

Views: 117

Answers (3)

plalx
plalx

Reputation: 43728

Some answers already have described how the problem can be solved with an explicit closure. Here's another example using Function.prototype.bind.

directionsService.route(dir_request, (function(result, status) {
    //the this keyword now refers to the correct route object
    console.log(this);

    if(status == google.maps.DirectionsStatus.OK){
        // render with custom logic depending on which route this is here
    }
}).bind(routes[i]));

EDIT:

Here's an efficient another way to do it without bind. Having the scope-safe function inline in the loop is not efficient since a new one has to be created on every iteration. It's better to create a function for that purpose and reuse it.

function createRouteCallback(route) {
    return function (result, status) {
        if(status == google.maps.DirectionsStatus.OK) {
            // render with custom logic depending on which route this is here

            //you can just use route here
            console.log(route);
        }
    };
}

Then in the loop you can simply do:

directionsService.route(dir_request, createRouteCallback(routes[i]));

Upvotes: 1

Arun P Johny
Arun P Johny

Reputation: 388416

You need to make use of closure scoped variables to do that. When you try to use i in the route callback function you are making use of a concept called as closures, where by a variable declared in a parent function will be accessible from an inner function.

But using closures in a loop has its own problems because the same instance of the closure variable will be shared between all the inner function created within the loop, so when the inner function is called you will get the last value assigned to the variable in the loop.

The solution here is to create a local closure inside the loop as shown below

directionsService = new google.maps.DirectionsService();
for (var i = 0; i < routes.length; i++) {
    (function (idx) {
        var dir_request = {
            origin: new google.maps.LatLng(routes[idx]['start_lat'], routes[idx]['stop_lng']),
            destination: new google.maps.LatLng(routes[idx]['end_lat'], routes[idx]['end_lng']),
            travelMode: google.maps.TravelMode.WALKING
        };
        directionsService.route(dir_request, function (result, status) {
            // the variable idx will refer to the array index for the current iteration
            if (status == google.maps.DirectionsStatus.OK) {
                // render with custom logic depending on which route this is here
            }
        })
    })(i)
}

Upvotes: 1

cxw
cxw

Reputation: 17051

Marka is right. Unless something strange is happening, all of your function's variables are available inside the callback function(result,status). See here for an explanation and some examples. Since you know dir_request when you create the callback, you can refer to it directly in the callback code. You should also be able to refer to i if you need to access the routes array.

If you have any trouble getting the value across, you can try adding a layer of indirection:

function makeCallback(dir_request) {
    return function(result,status) {
        //your code, and you can refer to dir_request
    };
}

then call

directionsService.route(dir_request, makeCallback(dir_request));

I had to do that once in a Greasemonkey script in order to make sure the variables I needed were locked in when the function() was created.

Upvotes: 0

Related Questions