Reputation: 2046
I'm looking to queue an arbitrary number of possibly optional function calls in JavaScript/jQuery. For example, I may need to ensure the user is authenticated and a cookie is set before running a second (or third, fourth, etc.) function or AJAX call.
I looked into doing this with the recently added jQuery.Deferred, but found that it doesn't matter what order the calls are fired in (true async style). Also, I read that once a Deferred instance has been resolved, it's not possible to un-resolve it.
Here's where I'm at with this at the moment. Initially, I was thinking of setting the Deferred instance to resolved, then un-resolving it if an optional function came up in the stack.
var d = $.Deferred(),
chained = d,
d.resolve(),
div = extra.find( "div:first" );
if ( extra.attr( "requires-auth" ) != undefined && !config.user_is_authenticated )
chained = chained.pipe( authenticate );
if ( div.length )
chained = chained.pipe( prepareExtra( div ) );
// When the two optional methods are resolved, show the content
chained.done( function() {
extra.fadeIn( 500 )
} );
My question is, what is the best way to queue (0 to N) AJAX calls in pure JavaScript/jQuery? (without using plug-ins).
Tak!
Edit 2: SOLVED! Here's some working examples of this, one w/o AJAX and one with: https://gist.github.com/1021429 https://gist.github.com/1021435
Upvotes: 6
Views: 3010
Reputation: 28200
Not tested, but jQuery.when should work nicely for this:
var q = [];
function queue(promise, callback) {
var ready = $.when.apply($, q); // resolves when all current elements
// of the queue have resolved
q.push(promise);
ready.done(function() {
q.shift();
var arg = Array.prototype.pop.call(arguments);
callback.call(promise, arg);
});
}
Upvotes: 0
Reputation: 140112
Try resolving your initial Deferred as the very last thing:
var d = $.Deferred(),
chained = d;
// optionally chain callbacks with chained = chained.pipe
if (condition) {
chained = chained.pipe(function () {
return $.ajax({...}); // Must return a new Promise
});
}
chained.done(function () {
// all chains should be processed now
});
d.resolve(); // finally, resolve the initial Deferred
Upvotes: 7
Reputation: 4295
I've handle this in the past by having an ajax call return the other scripts. To me, that is the best solution.
However, you wanted a pure js approach so I'll give that a shot.
var ScriptQueue = {
scripts: [],
readyToProcess: false,
timer: null,
stopTimer: function() {
clearTimeout(ScriptQueue.timer);
ScriptQueue.timer = null;
},
queue: function(functionToQueue) {
ScriptQueue.stopTimer();
ScriptQueue.scripts.push(functionToQueue);
ScriptQueue.processNext();
},
processNext: function() {
if (!readyToProcess || ScriptQueue.scripts.length == 0) {
ScriptQueue.timer = setTimeout(ScriptQueue.processNext, 30000); // try again in 30 sec
} else {
ScriptQueue.stopTimer();
var _function = ScriptQueue.scripts.shift();
_function.call();
ScriptQueue.processNext();
}
}
}
$(function(){
// queue some stuff
$('a').each(function() {
ScriptQueue.queue(function() {
console.info("running some link action");
} );
});
// authorization
$.ajax({
url: yourURL
success: function(data) {
if (data == "correct response") {
ScriptQueue.readyToProcess = true;
ScriptQueue.processNext();
}
}
})
});
I have no idea if this works (not tested) but I wanted to suggest a possible alternative to the Deferred
solution (which looks promising). Maybe it will lead to further discuss and maybe ignored.
Upvotes: 0
Reputation: 12228
Use a sequence helper:
https://github.com/michiel/asynchelper-js/blob/master/lib/sequencer.js
I had a similar problem: jQuery Ajax / .each callback, next 'each' firing before ajax completed
Upvotes: 0