Experimenting With jQuery's Queue() And Dequeue() Methods
jQuery comes with a number of built-in animation methods like slideUp() and slideDown(). While these appear to be packaged in their own methods, many of them are powered internally by the animate() method. And while the animate() method handles many of the effects, a string of sequential animations is controlled internally by jQuery's queuing mechanism. This queuing mechanism, while used primarily for the FX ("effects") queue, is also made available as part of the jQuery API. This API allows programmers to manage arbitrary queues for anything that needs to happen sequentially.
I have never used the queuing mechanism before, so I thought I would do some experimentation. There are two main methods that control a particular queue: queue() and dequeue(). While these methods can be accessed off of the jQuery namespace, they are designed to be associated with a given element or object. So, you can think of a queue as not just a series of events, but rather as a series of events that take place in the context of a given object.
To add an action to a queue, you have to tell the queue() method the name of the queue (the default queue is the "fx" queue) and the callback to be executed as the queue item. jQuery does not have any insight into how the queue items function; as such, we need to explicitly tell jQuery when to move to the next item in the queue. This can be done in two ways: first, you can call the dequeue() method on the given element with the given queue name. This works, but requires you to use the queue name which becomes yet another data point to maintain over time. The second, more optimal way, is to use the next() function that jQuery passes to your queue item callback. The next() function encapsulates the dequeue() method and the queue name into a single point of execution.
While the jQuery "fx" animation queue starts to execute immediately (dequeues itself), explicitly managed queues do not. As such, after we build up our queue items, we have to call dequeue() on the queue in order to kick off the series of sequential events. To see this in action, I have set up a small test page:
<!DOCTYPE HTML>
<html>
<head>
<title>jQuery Queue And Dequeue</title>
<script type="text/javascript" src="../jquery-1.4.2.js"></script>
<script type="text/javascript">
// When the DOM is ready, initialize scripts.
jQuery(function( $ ){
// Get a handle on the paragraph we want to update.
var para = $( "p:first" );
// Add the first queue item. Unlike the native animation
// methods, manually created queue items don't start
// executing right away - we have to manually call the
// dequeue() method at the end.
para.queue(
"testQueue",
function( next ){
para.html( "This is queue item #1: Hot" );
// Each queue method is passed a "next" method
// reference. This method encapsulates the
// name of the queue into a function. This way
// we can dequeue the current queue without
// having to know its name.
next();
}
);
// Delay the queue for a bit.
para.delay( 1500, "testQueue" );
// Add the next queue item.
para.queue(
"testQueue",
function( next ){
para.html( "This is queue item #2: Sexy" );
next();
}
);
// Delay the queue for a bit.
para.delay( 1500, "testQueue" );
// Add the next queue item.
para.queue(
"testQueue",
function( next ){
para.html( "This is queue item #3: Sleezy" );
next();
}
);
// ---------------------------------------------- //
// When we have our queue set up, we have to manually
// dequeue the first item to get the queue to start
// processing.
para.dequeue( "testQueue" );
});
</script>
</head>
<body>
<h1>
jQuery Queue And Dequeue
</h1>
<p>
This is where the queue output will go.
</p>
</body>
</html>
As you can see, I am using both the queue() method and the delay() method to build up my, "testQueue", queue. The delay() method, while typically used with animation queues, can be used to add an arbitrary pause to any queue event series. Once I have built up the queue, I use the dequeue() method to kick off the queue events.
jQuery's queuing mechanism seems very cool but, I am not sure what I would use it for outside of the native animation methods. I would certainly love to hear how other people might leverage this functionality.
Want to use code from this post? Check out the license.
Reader Comments
It's especially useful when have a series of asynchronous method calls that you want to execute serially, and don't want to maintain a chain of callbacks.
Say you want to execute first_async_action() followed by second_async_action(), followed by third_async_action(). You could do this:
<pre>
first_async_action(function(){
second_async_action(function(){
third_async_action(function(){
alert( 'done!' );
});
});
});
</pre>
Or if that looks messy (which it does) you could try something like this:
<pre>
function start(){
first_async_action( exec_second_async_action );
};
function exec_second_async_action(){
second_async_action( exec_third_async_action );
};
function exec_third_async_action(){
third_async_action( all_done );
};
function all_done(){
alert( 'done!' );
};
</pre>
start();
But what if you decide you need to execute third_async_action before second_async_action? either way, it's messy.
A good, flexibile, solution would be to do this kind of thing:
<pre>
function next(){
var action = actions[ idx++ ];
action && action();
};
var idx = 0,
actions = [
function(){
first_async_action( next );
},
function(){
second_async_action( next );
},
function(){
third_async_action( next );
},
function(){
alert( 'done!' );
}
];
next();
</pre>
Which is pretty much what jQuery's queuing methods do, internally.
Man, I wish there was comment code syntax highlighting.
@Cowboy,
Yeah, sorry about this code formatting in the comments. Never really got that nailed down quite nicely (read: at all). I'll try to make some time for that.
I see what you're saying about the sequential AJAX calls. Typically, in my apps, when I need to make sequential AJAX calls, the calls are highly coupled; meaning, the execution of the second relies heavily on the successful execution of the first. I think it would be much less likely that I would simply need to fire things sequentially.
Although, I suppose I could always clear the queue if one of the calls came back in a particular way. But even then, I wonder if my code would lose some readability?
@Ben, I like SyntaxHighlighter personally.. you can see it in my site's comments, plugin code examples, etc. Easy to use and flexible.
http://alexgorbatchev.com/wiki/SyntaxHighlighter
Regarding the queueing stuff, I actually wrote a plugin to manage queues, but it works a bit differently than jQuery's built in queue methods. You don't use it specifically on jQuery objects, for example.
http://benalman.com/projects/jquery-message-queuing-plugin/
Still, it really makes a lot of sense to use a managed queue internally for all these animation methods, because it eliminates the whole "ridiculous nested callback" scenario.. and of course, because the queue manager is iterating over an array of queued items, it's trivial to skip a queue item or stop the queue at any time.
BTW, here's my code as a gist for easier viewing:
http://gist.github.com/319568
@Ben and @Cowboy, thanks for sharing this info. I'm fairly new to jQuery. Thus far I have only really used it for dynamic drop down population, but I would like to branch out into using more of it.
@Cowboy,
I like the syntax highligher; unfortunately, my WYSIWYG editor (XStandard) does not allow for PRE tags. I am not sure of Script tags though, I should check into that.
Maybe I'll take a look at the way it works and see if I can port it over to the ColdFusion side. Right now, I do my formatting on Database-insert so it's only done once.
@James,
jQuery is wicked awesome! Once you start learning it, you'll be hooked!
@Cowboy,
Could you please explain the code you written?
I was trying to follow your example, but failed (( Here is my code. Probably you could tell me where I got something wrong
Thanks!
$('.child').bind('click', function(){
var $anchor = $(this);
function next(){
var action = actions[ idx++ ];
action && action();
};
var idx = 0,
actions = [
function(){
$(function (next) {$anchor.hide('fast');});
},
function(){
$(function (next) {$anchor.show('fast');});
},
function(){
$(function (next) {$anchor.hide('fast');});
},
function(){
alert( 'done!' );
}
];
next();
});
@tylik, it might be more helpful if you fork my gist on GitHub and ask your question there, so that we get the benefit of syntax highlighting, indenting, comments and revisions.
http://gist.github.com/319568
Brilliant example! Though its a very simple example, I used the concept for a complicated Ajax system which had multiple steps that should be executed serially reporting the status of each step to the DOM. Amazing and a big thanks to you and your great highlights.
@Mahesh,
Sounds like you found a really cool use of queue() and dequeue(). Glad that I might have provided some value in that.
Great information
Hello Ben,
This is just awesome, clear and easy to follow.
It helped me in a project I am doing,
Your site has become an authority on Ajax coding,
2 Thumbs up from me :)
Many Thanks,
Nader.
Excellent explanation, thanks for the post. I have only one question, how do i get to the last effect in queue without execute others effect.
Hi,
I have read lots of your tutorials, but i have never thanks you.
once more your explanation is very helpful and even for non english speaker it's very easy to understand.
I hope you will continue like this !
This is what i wanted about jquery queue, explained in a super simple manner. Thanks for sharing..
I just want to say that this post saved me!! I'm developing a blackjack game. I wanted the cards and scores to animate in the right order based on certain conditions (blackjack, bust, etc.).
To do this I was maintaining a complex and unreadable labrynth of callback functions.
Thanks to this simple little example, I fully understand how to manipulate the queue for separate elements and actions anywhere in my code.
@Adrien,
Awesome! I love it when code makes thing more simple, not more complex. Heck yeah!
I needed to queue some code recursively until a condition changed that was managed outside. Work wanted me to use the queue method rather than setInterval (no, I really don't know or care why). Had trouble, but got it figured out. Here is what worked for me (simple example) in case anyone else could use it.
Feel free to tell me if you see a more efficient way to achieve what I'm doing too.