Skip to content Skip to sidebar Skip to footer

(still) More Confusion Over Javascript Closures, Ajax, And Return Values

I'm trying to use JQuery .get() method and a javascript for loop to process some data from an external file. I've read about closures and return values from callbacks on stackover

Solution 1:

The problem isn't the closure, the problem is asynchronous functions. $.get() connects to a server, and runs its callback function when the server returns an answer. But $.get() completes once the request is sent, not when the response is returned. So your last two console.log() lines are running before the callback function is executed.

You can only access the headers and countryData variables once the callback function has executed, and the only place you know that has happened is inside the callback function itself. or other code that it calls.

Solution 2:

It's not a closure problem. It's just that the code lines doesn't execute in the order they are written.

It's a basic event programming problem: the end of the process is in the middle of the code. It's not a big problem once you're aware of it. You just have to write the end of your process at the right place.

In your case, things happen in this order:

Step 1. State variables are declared with this code:

var headers;  
var countryData = [];

Step 2. You call the server with this code

$.get(inCSV, <CALLBACK>)

At this point what's in the callback has no importance at all. It won't be executed until the server response comes back.

Step 3. You use the state variables with this code

console.log('outside',headers);  // output'outside undefined' ...!?
console.log('inside',countryData['Brazil']);    // output'outside undefined' ...!?

They are undefined, which is perfectly expectable because no code initialized them.

Step 4. Response comes back from the server:

    var lines = data.split('\r\n');
    for(var i=0;i<=lines.length-1;i++){
        var lineData = lines[i].split(',');
        if(i != 0){
            countryData[lineData[1]] = lineData.slice(2,lineData.length);
        } else {
            headers = lineData.slice(2,lineData.length);
        }
    }
    console.log('inside',headers);  // output'inside ["1971", "1972", "1973" ...'
    console.log('inside',countryData['Brazil']);  // output'inside ["56.4", "54.6", ..'

Solution 3:

$.get is asynchronous, meaning the rest of the script will not wait for it to complete. You can use the jQuery.Deferred class (docs) to mitigate this if you need more control than is offered by the success callback, or you can make the call synchronous (meaning the rest of the script will wait for it to finish before executing).

Synchronous AJAX Calls

You'll need to use $.ajax (docs), just pass-in async:false:

$.ajax({
  url: inCSV,
  async: false,
  success: function() { /* ... */ }
});

// code here will not execute until the ajax call above is complete

Deferred Objects

functionprocessData(inCSV) {
    var deferred = jQuery.Deferred();
    $.ajax({
        url: inCSV, 
        success: function(data){
            // do stuff
            deferred.resolve([data]);
        },
        error: function() {
            deferred.reject();
        }
    });
    return deferred;
}

processingData = processData(inCSV);

// any code that doesn't care about the processing results can go here// code that relies on headers or countryData must go in a block like this// you can add as many "done" blocks as you like
processingData.done(function(data){
    // mess with data here
});

Post a Comment for "(still) More Confusion Over Javascript Closures, Ajax, And Return Values"