patternjavascriptMinor
Wait until all files are loaded asynchronously, then run code
Viewed 0 times
untilallarewaitfilesloadedasynchronouslythencoderun
Problem
I'm an experienced programmer but not too great at JavaScript so I'm looking to see if I'm doing this 'right'. I want to have several files loaded in (Ajax or really AJAJ) and, once loaded, run some final code. My basic idea is:
Is this correct? Is it a reasonable solution, or are there better ways? Actually
Also, since I don't do much JavaScript these days (or all the cool kids calling it ECMAScript now?): will I run into trouble with the local function being passed around?
My current code has only one file being loaded, so I'm trying to extract the basic functionality here to keep the code DRY.
Meta: I don't know if this belongs here or on SO; feel free to move if I made the wrong choice.
var jobsToDo = 2; // global
function getAsync(url, func) {
var client = new XMLHttpRequest();
var handler = function() {
func();
jobsToDo--;
if(jobsToDo == 0)
allLoaded();
};
client.open('GET', url);
client.onreadystatechange = handler;
client.send();
}
getAsync('file1.json', function() {
// process file
});
getAsync('file2.json', function() {
// process file
});
function allLoaded() {
// ...
}Is this correct? Is it a reasonable solution, or are there better ways? Actually
onreadystatechange already feels like the wrong thing; is there a better handler that will wait until I get a 200, or should I expand my function above to exit unless the state is appropriate?Also, since I don't do much JavaScript these days (or all the cool kids calling it ECMAScript now?): will I run into trouble with the local function being passed around?
My current code has only one file being loaded, so I'm trying to extract the basic functionality here to keep the code DRY.
Meta: I don't know if this belongs here or on SO; feel free to move if I made the wrong choice.
Solution
1) Nah, the kids still call it JavaScript, but ECMAScript works too. More power to you for knowing both names :)
2) Yes, I'd say you should wait for a less ambiguous state to occur before considering the request done. But there's not really anything better than
Other than that, though, your code's good. The hardcoded
You could take a look at the futures and promises pattern. There are several JS-implementations of this you can use.
jQuery has such an implementation (via the Deferred object) built into it's Ajax-system, allowing you to do, say:
Point is that Deferred lets you attach handlers asynchronously - the requests are sent the moment you call
Now, I know this is all jQuery, and perhaps you don't want to use that. If so, more power to you (again). It is however a neat approach and a simple API - or (in my opinion) one to emulate if you so choose.
Seeing @Kevindra's simple answer (go upvote!), I'm reminded of a different pattern I've used before:
2) Yes, I'd say you should wait for a less ambiguous state to occur before considering the request done. But there's not really anything better than
onreadystatechange (as far as I recall) to check for the request's success. So you still have to code a "ready-state change" handler, just make it check client.status === 200 (and possibly some headers and whatnot) before deciding what to call next.Other than that, though, your code's good. The hardcoded
2 would need to go for this to be of general use, but the basic idea of decrementing a counter is solid.You could take a look at the futures and promises pattern. There are several JS-implementations of this you can use.
jQuery has such an implementation (via the Deferred object) built into it's Ajax-system, allowing you to do, say:
var file1 = $.getJSON("file1.json"), // $.getJSON returns a Deferred
file2 = $.getJSON("file2.json"),
all = $.when(file1, file2); // and $.when groups several Deferreds
// example usage - you can do the same for the individual files
all.done(function () {
// something to call when all files have been successfully loaded
});
all.fail(function () {
// something to call in case one or more files fail
});
all.always(function () {
// something to always call (like, say, hiding a "loading" indicator)
});Point is that Deferred lets you attach handlers asynchronously - the requests are sent the moment you call
getJSON, but you can attach handlers whenever. And you can add several handlers to the same state, if you want. If the response has already been received, the handler will simply be called immediately.Now, I know this is all jQuery, and perhaps you don't want to use that. If so, more power to you (again). It is however a neat approach and a simple API - or (in my opinion) one to emulate if you so choose.
Seeing @Kevindra's simple answer (go upvote!), I'm reminded of a different pattern I've used before:
// a simple factory function
function makeCounter(limit, callback) {
return function () {
if( --limit === 0 ) {
callback();
}
}
}
// ....
// make a "done" function set up to expect 2 invocations
// before calling its callback
var done = makeCounter(2, function () {
// all done!
});
getAsync("file1.json", function () {
// process file and call the done() function
done();
)};
getAsync("file2.json", function () {
// process file and call the done() function
done();
)};Code Snippets
var file1 = $.getJSON("file1.json"), // $.getJSON returns a Deferred
file2 = $.getJSON("file2.json"),
all = $.when(file1, file2); // and $.when groups several Deferreds
// example usage - you can do the same for the individual files
all.done(function () {
// something to call when all files have been successfully loaded
});
all.fail(function () {
// something to call in case one or more files fail
});
all.always(function () {
// something to always call (like, say, hiding a "loading" indicator)
});// a simple factory function
function makeCounter(limit, callback) {
return function () {
if( --limit === 0 ) {
callback();
}
}
}
// ....
// make a "done" function set up to expect 2 invocations
// before calling its callback
var done = makeCounter(2, function () {
// all done!
});
getAsync("file1.json", function () {
// process file and call the done() function
done();
)};
getAsync("file2.json", function () {
// process file and call the done() function
done();
)};Context
StackExchange Code Review Q#45541, answer score: 4
Revisions (0)
No revisions yet.