views:

1132

answers:

9

Hey all. I have, what appears to be, a trivial problem. I have the following JavaScript:

$(function() {
    var r = GetResults();

    for(var i = 0; i < r.length; i++) {
        // Do stuff with r
    }
});

function GetResults() {
   $.getJSON("/controller/method/", null, function(data) {
       return data;
   });
}

Due to the fact that I'm calling a method asynchronously, the script continues executing and when it encounters the for loop, r obviously isn't going to have a value yet. My question is: when I have a method that is doing an asynchronous operation, and I'm dependent on the data it returns back in the main block, how do I halt execution until the data is returned? Something like:

var r = GetResults(param, function() {

});

where the function is a callback function. I cannot move the for loop processing into the callback function of the JSON request because I am reusing the functionality of GetResults several time throughout the page, unless I want to duplicate the code. Any ideas?

+4  A: 

move your "do stuff with r" block into your $.getJSON callback. you can't do stuff with r until it has been delivered, and the first opportunity you'll have to use r is in the callback... so do it then.

$(function() {
    var r = GetResults();  
});

function GetResults() {
   $.getJSON("/controller/method/", null, function(data) {
       for(var i = 0; i < data.length; i++) {
           // Do stuff with data
       }
       return data;
   });
}
Ty W
Thank you for the response. The reason I split GetResults out was that I am reusing that functionality several times throughout the page. I should edit my post to state that.
if you need to do the same processing on data coming from multiple places, just replace your callback function with a named function and do the processing there.$.getJSON("/controller/method/", null, processData(data));function processData(d) { ... }
Ty W
+1  A: 

You could do this:

$(function() {
    PerformCall();        
});

function PerformCall() {
   $.getJSON("/controller/method/", null, function(data) {
       for(var i = 0; i < data.length; i++) {
        // Do stuff with data
       }
   });
}
Quintin Robinson
A: 

This is not possible.

Either you make your function synchronous or you change the design of your code to support the asynchronous operation.

Georg
A: 

Move the data processing into the callback:

$(function() {
    GetResults();
});

function GetResults() {
   $.getJSON("/controller/method/", null, function(data) {

       for(var i = 0; i < data.length; i++) {
           // Do stuff with data
       }
   });
}
ctford
this wont work. your return statement will return from the callback, not from GetResults. This solution will never work as it does not use callbacks properly
mkoryak
@mkoryak Thanks. I've removed the return from the callback.
ctford
+4  A: 

Ajax already gives you a callback, you are supposed to use it:

function dostuff( data ) {
    for(var i = 0; i < data.length; i++) {
        // Do stuff with data
    }
};
$(document).ready( function() {
    $.getJSON( "/controller/method/", null, dostuff );
});
THC4k
+2  A: 

The short answer is that you can't block on an asynchronous operation...which is of course, the meaning of "asynchronous".

Instead, you need to change your code to use a callback to trigger the action based on the data returned from the $.getJSON(...) call. Something like the following should work:

$(function() {
  GetResults();
});

function GetResults() {
  $.getJSON("/controller/method/", null, function(data) {
    for(var i = 0; i < data.length; i++) {
      // Do stuff with data
    }
  });
}
rcoder
+1  A: 

Given your updated requirements ...

I cannot move the for loop processing into the callback function of the JSON request because I am reusing the functionality of GetResults several time throughout the page, unless I want to duplicate the code. Any ideas?

... you could modify GetResults() to accept a function as a parameter, which you would then execute as your $.getJSON callback (air code warning):

$(function() {
    GetResults(function(data) {
        for(var i = 0; i < data.length; i++) {
            // Do stuff with data
        }
    });
});

function GetResults(callback) {
   $.getJSON("/controller/method/", null, callback);
}

As you can see from the general tide of answers, you're best off not trying to fight the asynchronous jQuery programming model. :)

Jeff Sternal
A: 

You can have a callback with parameters that should work nicely...

$(function() {
    GetResults(function(data) {
      for(var i = 0; i < data.length; i++) {
        // Do stuff with data
      }
    });

});

function GetResults(func) {
   $.getJSON("/controller/method/", null, func);
}
Josh Stodola
+1  A: 

I've run into something similar before. You'll have to run the ajax call synchronously.

Here is my working example:

$.ajax({
    type: "POST",
    url: "/services/GetResources",
    contentType: "application/json; charset=utf-8",
    dataType: "json",
    data: '{resourceFileName:"mapedit",culture:"' + $("#lang-name").val() + '"}',
    cache: true,
    async: false, // to set local variable
    success: function(data) {
        localizations = data.d;
    }
});
ScottE
This certainly works, but it blocks the UI thread, doesn't it? (Perhaps that's acceptable, though it doesn't seem necessary given the information we have so far.)
Jeff Sternal
Yes, it does block, but of course only for the duration of the request. I don't see any other way around it, given what you're looking for. I went down the same road and went the sync route.
ScottE