km65
km65

Reputation: 21

Need help creating async function with callback

I have a JSON object with some entries (Appointments) each thereof an "location id". Then I loop trough these entries and emit a request to my nodeJS server by socketIO to get data from document with the location id.

At the end I need an array with the data of lat/lng to create some marker on a map.

Here is the code:

//controller for showing map
.controller('MapCtrl', function($scope, socket){

    socket.emit('getApp', staticUserid);

        socket.on('getApps', function (appdata) {
                var locArr = [];
                for (var i = 0; i < appdata.length; i++) {
                    if (appdata[i].locationid != '') {
                        locArr.push(appdata[i].locationid);
                    }
                }
                var LatLngArr = [];
                for (var j = 0; j < locArr.length; j++) {
                    socket.emit('getLocation', locArr[j]);
                    socket.on('getLoc', function (locData) {
                        console.log('received lat/lng: ' + locData.lat + '/' + locData.lng);
                        if (!LatLngArr[j]) LatLngArr[j] = []
                        LatLngArr[j][0] = locData.lat;
                        LatLngArr[j][1] = locData.lng;
                    });
                }
                //console.log('test:'+LatLngArr[0][0]);
        });

    var newMarkers = [[52.549678, 13.3879516],[52.5442992, 13.352809],[52.5186283,13.3761181]]; // this should be the generated array
    var newCenter = [52.549678, 13.3879516];
    createMap(newCenter,newMarkers);

})

The problem is, that the var LatLngArr isn't defined out of the...

socket.on('getLoc', function (locData)

It would be very nice if somebody can help me :-)

Thanks so much!

Upvotes: 0

Views: 81

Answers (3)

philonous
philonous

Reputation: 4061

The reason why your code fails is because of the asynchronous nature of the line:

socket.on('getLoc', function (locData)

So LatLngArr actually is defined when you register the callback, but it is not anymore at the time the callback is called. The reason for that is that JavaScript uses Closures.

You may have a look at this thread: How do JavaScript closures work?

Hence you have to wrap it in an IIFE. This way the function is executed immediately such that the value of j behaves as you intended, because it is not longer referring to the j of the outer scope.

Edit: A very nice explanation can be found in Item 13 of David Hermann's "Effective Javascript" – a book I totally can recommend. If you have problems understanding it, a look at this thread may help.

Upvotes: 0

Jaromanda X
Jaromanda X

Reputation: 1

If you can use Promises

.controller('MapCtrl', function($scope, socket){

    socket.emit('getApp', staticUserid);

    socket.on('getApps', function (appdata) {
        var locArr = [];
        for (var i = 0; i < appdata.length; i++) {
            if (appdata[i].locationid != '') {
                locArr.push(appdata[i].locationid);
            }
        }
        var LatLngArr = [];
        var promises = [];
        for (var j = 0; j < locArr.length; j++) {
            promises[j] = (function(captured_j) {
                return new Promise(function(resolve, reject) {
                    socket.emit('getLocation', locArr[captured_j]);
                    socket.on('getLoc', function (locData) {
                        console.log('received lat/lng: ' + locData.lat + '/' + locData.lng);
                        if (!LatLngArr[captured_j]) LatLngArr[captured_j] = []
                        LatLngArr[captured_j][0] = locData.lat;
                        LatLngArr[captured_j][1] = locData.lng;
                        resolve({index: captured_j, result: LatLngArr[captured_j]});
                    });
                });
            }(j));
        }
        Promise.all(promises).then(function(arr) {
            // ******************************************
            // ******************************************
            // arr is an array of {index: #, result [lat, lng]} - but you can also use LatLngArr
            // ******************************************
            // ******************************************
        });
    });
    var newMarkers = [[52.549678, 13.3879516],[52.5442992, 13.352809],[52.5186283,13.3761181]]; // this should be the generated array
    var newCenter = [52.549678, 13.3879516];
    createMap(newCenter,newMarkers);
})

Upvotes: 1

Jaromanda X
Jaromanda X

Reputation: 1

try this

for (var j = 0; j < locArr.length; j++) {
    (function(captured_j) {
        socket.emit('getLocation', locArr[captured_j]);
        socket.on('getLoc', function (locData) {
            console.log('received lat/lng: ' + locData.lat + '/' + locData.lng);
            if (!LatLngArr[captured_j]) LatLngArr[captured_j] = []
            LatLngArr[captured_j][0] = locData.lat;
            LatLngArr[captured_j][1] = locData.lng;
            //
            // the required result
            //
            if (j === 0) {
                console.log('test:'+LatLngArr[0][0]);
            }
            //
        });
    }(j));
}

Upvotes: 0

Related Questions