I’m finding myself writing a lot of spaghetti in Javascript when I have to deal with asynchronous
applications (specially when dealing with OpenSocial code where all the data has to be obtained
through JS). The usual pattern is something like:
- User logs into the application for the first time, get his data.
- Do A on his data (e.g. get his friends by sending a request to the server).
- Do B on this data (e.g. send his friends to the server for some processing).
- Do C on his data (e.g. check that the server response is valid so we can do something else).
Note that this sequential execution path (1 => 2 => 3 => 4) doesn’t fit well the async. nature of
Ajax so the user ends up waiting for a long time and the code turns into a mess since every step
depends on the previous ones.
An example with code:
gadgets.util.registerOnLoadHandler(setupUser())
...
function setupUser() {
var req = [get data and setup request]
req.send(some_url, some_data, function(response) { getFriendsFor(response.user) });
}
function getFriendsFor(user) {
var friends = [get friends from user]
var req = [setup request]
req.send(some_other_url, some_other_data, function(response { validateFriendsResponse(response.friends) });
}
function validateFriendsResponse(friends) {
if (friends.valid())
...
loadCanvas();
}
You can see that each function depends on the previous one, and what’s worse, it has to be called in
a specific order to be useful. It gets worse when you have to add stuff like showing/hiding loading
screens and other gimmicks while the user waits.
How would you go about fixing this?
One option might be to have a variable that shows the current state, and have a “controller” function that is always the AJAX callback function. Based on the current state, the controller function will call the next function in line. To simplify the controller function, I’d probably store the sequence of functions to call in a Javascript object, so all the controller function is doing is a lookup and a pass off to the next function in the sequence. This approach might be facilitated by having a single Javascript object that is always the parameter to the function (and contains all of the data that was returned by earlier AJAX calls.
Example: