Chuck Bergeron
Chuck Bergeron

Reputation: 2046

Queuing optional AJAX/function calls in jQuery ($.Deferred)?

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

Answers (4)

Tgr
Tgr

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

Ateş Göral
Ateş Göral

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

natedavisolds
natedavisolds

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

Related Questions