Variables set during $.getJSON function only accessible within function
$.getJSON
is asynchronous. That is, the code after the call is executed while $.getJSON
fetches and parses the data and calls your callback.
So, given this:
a();
$.getJSON("url", function() {
b();
});
c();
The order of the calls of a
, b
, and c
may be either a b c
(what you want, in this case) or a c b
(more likely to actually happen).
The solution?
Synchronous XHR requests
Make the request synchronous instead of asynchronous:
a();
$.ajax({
async: false,
url: "url",
success: function() {
b();
}
});
c();
Restructure code
Move the call to c
after the call to b
:
a();
$.getJSON("url", function() {
b();
c();
});
Remember that when you supply a callback function, the point of that is to defer the execution of that callback until later and immediately continue execution of whatever is next. This is necessary because of the single-threaded execution model of JavaScript in the browser. Forcing synchronous execution is possible, but it hangs the browser for the entire duration of the operation. In the case of something like $.getJSON, that is a prohibitively long time for the browser to stop responding.
In other words, you're trying to find a way to use this procedural paradigm:
var foo = {};
$.getJSON("url", function(data) {
foo = data.property;
});
// Use foo here.
When you need to refactor your code so that it flows more like this:
$.getJSON("url", function(data) {
// Do something with data.property here.
});
"Do something" could be a call to another function if you want to keep the callback function simple. The important part is that you're waiting until $.getJSON finishes before executing the code.
You could even use custom events so that the code you had placed after $.getJSON subscribes to an IssuesReceived event and you raise that event in the $.getJSON callback:
$(document).ready(function() {
$(document).bind('IssuesReceived', IssuesReceived)
$.getJSON("url", function(data) {
$(document).trigger('IssuesReceived', data);
});
});
function IssuesReceived(evt, data) {
// Do something with data here.
}
Update:
Or, you could store the data globally and just use the custom event for notification that the data had been received and the global variable updated.
$(document).ready(function() {
$(document).bind('IssuesReceived', IssuesReceived)
$.getJSON("url", function(data) {
// I prefer the window.data syntax so that it's obvious
// that the variable is global.
window.data = data;
$(document).trigger('IssuesReceived');
});
});
function IssuesReceived(evt) {
// Do something with window.data here.
// (e.g. create the drag 'n drop interface)
}
// Wired up as the "drop" callback handler on
// your drag 'n drop UI.
function OnDrop(evt) {
// Modify window.data accordingly.
}
// Maybe wired up as the click handler for a
// "Save changes" button.
function SaveChanges() {
$.post("SaveUrl", window.data);
}
Update 2:
In response to this:
Does anyone have an idea how I should be blocking user interaction while this is going on? Why is it such a concern? Thanks again for all the responses.
The reason that you should avoid blocking the browser with synchronous AJAX calls is that a blocked JavaScript thread blocks everything else in the browser too, including other tabs and even other windows. That means no scrolling, no navigation, no nothing. For all intents and purposes, it appears as though the browser has crashed. As you can imagine, a page that behaves this way is a significant nuisance to its users.
maybe this work, works to me.. :)
$variable= new array();
$.getJSON("url", function(data){
asignVariable(data);
}
function asignVariable(data){
$variable = data;
}
console.log($variable);
Hope it help you.. :)
You could approach this with promises:
var jsonPromise = $.getJSON("url")
jsonPromise.done(function(data) {
// success
// do stuff with data
});
jsonPromise.fail(function(reason) {
// it failed... handle it
});
// other stuff ....
jsonPromise.then(function(data) {
// do moar stuff with data
// will perhaps fire instantly, since the deferred may already be resolved.
});
It is pretty straight forward and a viable way to make async code feel more imperative.
"But this is done via subsequent interactions on the page, and I cannot foresee what the user will want to do with the JSON object within the callback."
The callback is your opportunity to set the screen up for the user's interaction with the data.
You can create or reveal HTML for the user, and set up more callbacks.
Most of the time, none of your code will be running. Programming an Ajax page is all about thinking about which events might happen when.
There's a reason it's "Ajax" and not "Sjax." There's a reason it's a pain to change from async to sync. It's expected you'll do the page async.
Event-driven programming can be frustrating at first.
I've done computationally intensive financial algorithms in JS. Even then, it's the same thing--you break it up into little parts, and the events are timeouts.
Animation in JavaScript is also event driven. In fact, the browser won't even show the movement unless your script relinquishes control repeatedly.