AjaxQueue and jQuery 1.3

800px-Queue_algorithmn The website that I work on for the majority of my time had been running on the rather out of date jQuery 1.2 for quite some time. I recently found myself with a little bit of downtime and decided it was a good time to see if we could upgrade and take advantage of all the speed improvements that the latest version gives us.

On the most part, the upgrade was fairly painless. Most things worked without change but one particular plug-in did not – AjaxQueue. This is a great little control that acts as an add-on to the ajax method of jQuery, allowing a little more control over how concurrent calls are processed. It gives three modes of operation but for us, the most useful of these are abort and queue and from the names it is fairly obvious what they do.

After I upgraded to jQuery 1.3, it did not take me long to realise that the queue mode was broken. I briefly looked around for an alternative plug-in but found that all of them would require too much change to our existing controls. There was no alternative, I had to open up the hood of AjaxQueue and see what had gone wrong.

What I found was that there must of been a change to a method that the plug-in relied on – a method that it seemed, only became publicly available in version 1.3 – queue. Although the function was similar, the conclusion that I came to was that prior to version 1.3, the queue function automatically de-queued any function added and processed it. When using the latest jQuery, the ajax calls were queued but there was no code to start the processing of the queue.

It was slightly more complex I thought it would be to get it going – I had to ensure the queue was only started once. I finally came up with a solution and a newly revived plug-in, the code of which is below. As the original plugin has not been touched for quite some time (2007), I thought it ok to post my modified version here.

(function($) {
 
    var ajax = $.ajax,
        pendingRequests = {},
        synced = [],
        syncedData = [],
        ajaxRunning = [];
 
 
    $.ajax = function(settings) {
        // create settings for compatibility with ajaxSetup
        settings = jQuery.extend(settings, jQuery.extend({}, jQuery.ajaxSettings, settings));
 
        var port = settings.port;
 
        switch (settings.mode) {
            case "abort":
                if (pendingRequests[port]) {
                    pendingRequests[port].abort();
                }
                return pendingRequests[port] = ajax.apply(this, arguments);
            case "queue":
                var _old = settings.complete;
                settings.complete = function() {
                    if (_old) {
                        _old.apply(this, arguments);
                    }
                    if (jQuery([ajax]).queue("ajax" + port).length > 0) {
                        jQuery([ajax]).dequeue("ajax" + port);
                    } else {
                        ajaxRunning[port] = false;
                    }
                };
 
                jQuery([ajax]).queue("ajax" + port, function() {
                    ajax(settings);
                });
 
                if (jQuery([ajax]).queue("ajax" + port).length == 1 && !ajaxRunning[port]) {
                    ajaxRunning[port] = true;
                    jQuery([ajax]).dequeue("ajax" + port);
                }
 
                return;
            case "sync":
                var pos = synced.length;
 
                synced[pos] = {
                    error: settings.error,
                    success: settings.success,
                    complete: settings.complete,
                    done: false
                };
 
                syncedData[pos] = {
                    error: [],
                    success: [],
                    complete: []
                };
 
                settings.error = function() { syncedData[pos].error = arguments; };
                settings.success = function() { syncedData[pos].success = arguments; };
                settings.complete = function() {
                    syncedData[pos].complete = arguments;
                    synced[pos].done = true;
 
                    if (pos == 0 || !synced[pos - 1])
                        for (var i = pos; i < synced.length && synced[i].done; i++) {
                        if (synced[i].error) synced[i].error.apply(jQuery, syncedData[i].error);
                        if (synced[i].success) synced[i].success.apply(jQuery, syncedData[i].success);
                        if (synced[i].complete) synced[i].complete.apply(jQuery, syncedData[i].complete);
 
                        synced[i] = null;
                        syncedData[i] = null;
                    }
                };
        }
        return ajax.apply(this, arguments);
    };
 
})(jQuery);

29 Comments to “AjaxQueue and jQuery 1.3”

  1. Dan 12 September 2012 at 10:17 pm #

    Wondering im using drupal with jquery 1.3 and i would like to apply this function it would be a lifesaver
    Can anyone give an example of a request called using this plugin?
    thx in advance

  2. Swader 18 April 2011 at 2:30 am #

    Actually yes, but I am unsure as to how to reproduce it. I simply.. used it.

    Granted, I used it on a site-wide level to define all ajax calls as “abort”, so that might have been it? I’ll try and whip up an example today.

  3. dan 17 April 2011 at 2:17 pm #

    @swader I threw together a quick test for using jQuery 1.5 and it seemed to work correctly. Have you found a situation in which the plugin does not work with jQuery 1.5? I am keen to know.

  4. Swader 13 April 2011 at 9:56 am #

    Awesome, that’s all anyone could ask. Thanks a lot

  5. dan 13 April 2011 at 9:54 am #

    @swader I have yet to find the time to update this plugin for the new versions of jQuery. The entire ajax functionality has been rewritten, so I am unsure yet what needs to be done to make it work. I will need to update it for projects I am working on, so I’ll post it here as soon as I have done it.

  6. Swader 13 April 2011 at 1:28 am #

    Any chance of an update for $1.5?

  7. Swader 9 February 2011 at 8:02 am #

    Mighty nice, cheers, I’ll give that a shot!

  8. dan 7 February 2011 at 10:05 am #

    @Swader I have added a subscribe checkbox to the comments form. Hope that helps. In answer to your previous question – the calls are held on a queue. You could empty the queue manually with something like: jQuery([ajax]).clearQueue(“ajaxTHEPORT”). Replace ‘THEPORT’ with whatever your port is named. I have not tested this but I think it should work.

  9. Swader 7 February 2011 at 8:42 am #

    Is.. umm.. is it possible to subscribe to the comments so I know when and if someone answers my comment?

  10. Swader 6 February 2011 at 9:22 am #

    Is there a way to cancel the queue?
    For example, hundreds of calls are going on… a user notices an error in one (as the statues are being updated on screen live) and wants to stop the queue completely and empty it. Can it be done?

  11. scriptologist 26 January 2011 at 4:17 pm #

    the best, d matabang. Thanks! thanks! thanks!

  12. Luann Potts 23 December 2010 at 7:22 pm #

    edit: at least for the “queue” mode it can be fixed if you
    change all calls of “ajax.apply(this, arguments)” to
    ajax.apply(settings.context, arguments) The “sync” mode seems to be
    more work…

  13. Tuan Jinn VN 7 December 2010 at 3:28 am #

    I have a question: is there anyway to pause, or cancel the queued ajax requests from another js call?

    Let say I have to send 10 heavy requests, at request no 5, I want to pause those heavy requests or even abort them, to send another one.

    Do you have an idea?
    Tuan Jinn

  14. Tuan Jinn 7 December 2010 at 1:36 am #

    You rocks, it fix my errors, the queue-related issues has made everything stop working. I dont know what actually happened since it had worked the day before. It seemed that we updated jQuery and problem occurred.
    Thanks

  15. Manuel 26 November 2010 at 11:25 am #

    For anyone using the sync mode, the callbacks are being fired regardless of the response status. That is, the “success” callback will be called even if there’s an error (eg, request timed out).

    To fix this, change the following 3 lines (within the sync’s “for” loop:

    if ( synced[i].error && syncedData[i].error.length) synced[i].error.apply( jQuery, syncedData[i].error );
    if ( synced[i].success && syncedData[i].success.length) synced[i].success.apply( jQuery, syncedData[i].success );
    if ( synced[i].complete && syncedData[i].complete.length) synced[i].complete.apply( jQuery, syncedData[i].complete );

    Basically adding syncedData[i].*CALLBACK*.length to the if.

    HTH

  16. chris 12 August 2010 at 1:17 am #

    edit:
    at least for the “queue” mode it can be fixed if you change all calls of “ajax.apply(this, arguments)” to ajax.apply(settings.context, arguments)
    The “sync” mode seems to be more work…

  17. chris 12 August 2010 at 1:07 am #

    There is a bug when using the “context” option from the original jQuery.ajax . In the callbacks the false “this” reference is set.

  18. You rock! 12 July 2010 at 2:13 am #

    i had my crappy solution… but your code rocks! thx!

  19. agentphoenix 28 June 2010 at 2:58 am #

    I’ve been looking high and low through all kinds of Ajax queuing stuff and this looks promising, but I have a page I’m working on that can have up to 8 Ajax requests, but the user determines how many requests there are. Is there a way with something like this to know when the queue has finished running?

  20. dan 23 June 2010 at 2:48 pm #

    @mat. Happy to have sorted that for you ;)

  21. mat. 23 June 2010 at 2:27 pm #

    Forget it, it’s work like a charm. My mistake.

  22. mat. 23 June 2010 at 11:33 am #

    Hi Dan,

    Nice work!

    I use your solution on my local website, I use jQuery 1.4.2. There is a problem on your solution, the queue length can increase but never decrease, so it’s work for all ajax request which are sent before the first request is completed otherwise it’s blocked. It’s due to the fact that you use the queue length to permut the “ajaxRunning[port]” variable. But the queue length doesn’t reflact the number of request which are not yet sent but reflact the number of ajax request call.

    Anyway to solve it?

  23. dan 19 May 2010 at 9:43 am #

    Yes, thats exactly what it does. It also adds a ‘port’ option, which allows you to group requests together.

  24. Bruce 19 May 2010 at 2:53 am #

    So, do I understand correctly that this would add a new option ‘mode’ to standard .ajax() calls, to which I should pass either ‘abort’, ‘queue’ or ‘sync’?

  25. Chris 7 May 2010 at 9:42 am #

    Thank you! I was having the usual AJAX race condition related problems, and all I had to do was simply drop this in, and it fixed it (the autocomplete plugin we use, the “Bassistance” one, already takes advantage of AJAX queue).

  26. jkorbes 10 March 2010 at 9:38 am #

    Exactly what I was looking for! Thanks very much for posting.

  27. dan 1 February 2010 at 9:50 am #

    @Michael Yes, adding the additional ‘mode’ option is what the original ajaxqueue plugin did. It also added the ‘port’ setting, so you can distinguish between different ajax calls.

  28. Michael 1 February 2010 at 3:39 am #

    So, do I understand correctly that this would add a new option ‘mode’ to standard .ajax() calls, to which I should pass either ‘abort’, ‘queue’ or ‘sync’?

  29. arnon 11 January 2010 at 9:33 pm #

    Thank! just what I needed. I got as far as understanding that the queue was not starting to be processed and you came just in time for me.

    A well done job it is.


Leave a Reply