Himanshu SharmaSoftware engineer
Let’s talk

jQuery · Lesson 32 of 45

The animation queue: delay and stop

Understand the jQuery animation queue, chain effects in order, pause with .delay(), stop runaway animations with .stop(), and wait with .promise().

12 minEffects and animationUpdated 23 October 2026

By the end of this lessonRun effects in sequence or in parallel, stop animations piling up on repeated clicks, and run code after all animations have finished.

Effects wait in line

Each element has an animation queue. When you start an effect while another is running on the same element, the new one waits its turn. Chaining effects therefore plays them in order:

html
<div id="box" style="width: 120px; height: 50px; background: #fb7185"></div>

<script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
<script>
  const start = Date.now();
  const at = label => console.log(label, "after about", Math.round((Date.now() - start) / 100) * 100, "ms");

  $("#box")
    .slideUp(200, () => at("1. slid up"))
    .slideDown(200, () => at("2. slid down"))
    .animate({ width: 240 }, 200, () => at("3. widened"));
  at("0. chain set up");
</script>
result
0. chain set up after about 0 ms
1. slid up after about 200 ms
2. slid down after about 400 ms
3. widened after about 600 ms

The whole chain was set up instantly (step 0), and then each effect ran when the previous one finished, about 200ms apart. Effects on different elements do not share a queue, so they run at the same time.

Pausing with .delay()

.delay(ms) adds a pause to the queue:

html
<p id="toast" style="display: none; background: #1e293b; color: white; padding: 8px">Saved</p>

<script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
<script>
  $("#toast")
    .fadeIn(150)
    .delay(500)
    .fadeOut(150, function () {
      console.log("toast shown, waited, and hidden; display =", $(this).css("display"));
    });
</script>
result
Saved

That is a complete "toast" notification in three lines: appear, wait, disappear.

.delay() only delays queued effects. Instant methods such as .css(), .text() or .addClass() do not wait in the queue; they run straight away:

html
<p id="msg">Hello</p>

<script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
<script>
  $("#msg").fadeOut(200).delay(300).css("color", "red");
  console.log("colour straight away:", $("#msg").css("color"));

  // To wait, put instant changes in the queue
  $("#msg").delay(300).queue(function (next) {
    $(this).text("Changed after the queue");
    console.log("queued change ran");
    next();
  }).fadeIn(200);
</script>
result
colour straight away: rgb(255, 0, 0)

.queue(fn) adds your own function to the queue. Call the next function it receives, or the queue stops there.

Stopping runaway animations

Queues cause a classic bug. Hover over a menu many times quickly, and each hover adds another slide to the queue, so the menu keeps opening and closing long after the pointer has gone:

html
<button id="bad">Toggle A (queues)</button> <button id="good">Toggle B (stops first)</button>
<button id="fast">Click both four times quickly</button>
<div id="a" style="height: 40px; background: #93c5fd">Panel A</div>
<div id="b" style="height: 40px; background: #86efac">Panel B</div>

<script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
<script>
  $("#bad").on("click", () => $("#a").slideToggle(300));
  $("#good").on("click", () => $("#b").stop(true).slideToggle(300));

  $("#fast").on("click", function () {
    for (let i = 0; i < 4; i++) { $("#bad").trigger("click"); $("#good").trigger("click"); }
    console.log("A queue:", $("#a").queue().length, "| B queue:", $("#b").queue().length);
  });
</script>
result
A queue: 4 | B queue: 1

After four quick clicks, Panel A has four slides in its queue (the running one plus three waiting) and will keep moving for over a second. Panel B only ever has the latest one, because .stop(true) cleared the queue before each new slide. Click the first two buttons quickly yourself in the editor to feel the difference.

The forms of stopping:

Call Effect
.stop() stop the current animation where it is; the next queued one starts
.stop(true) stop the current one and clear the queue
.stop(true, true) as above, but jump the current one to its end values
.finish() stop everything and jump all queued animations to their end values

.stop(true) before starting an effect in a hover or click handler is the standard fix.

Waiting for everything: .promise()

A callback runs once per element. If you fade out ten items and want to do something once all have finished, use .promise():

html
<ul><li>One</li><li>Two</li><li>Three</li></ul>

<script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
<script>
  let callbacks = 0;
  $("li").fadeOut(200, () => callbacks++)
    .promise()
    .then(function () {
      console.log("per-element callbacks:", callbacks);
      console.log("all", this.length, "items have faded; now update the counter once");
    });
</script>
result
per-element callbacks: 3
all 3 items have faded; now update the counter once

The callback ran three times, but the .then() function ran once, with this set to the whole selection. The Ajax section uses the same promise idea for requests.

Running effects in parallel

Two effects on the same element normally queue. To run an .animate() at the same time as another, pass queue: false:

javascript
$("#card")
  .slideDown(300)
  .animate({ opacity: 1 }, { duration: 300, queue: false });

Common mistakes

  • Expecting .delay() to delay .css() or .text().
  • Starting effects from hover or click without .stop(true), so they pile up.
  • Using a per-element callback for code that should run once.
  • Forgetting next() in .queue(), which freezes later effects on that element.

Exercise

Make a "Save" button that shows a message, waits 400ms and fades the message out. Repeated clicks must not queue up several messages: each click should restart the sequence.

Show one possible answer
html
<button id="save">Save</button>
<p id="saved" style="display: none">Saved!</p>

<script src="https://code.jquery.com/jquery-4.0.0.min.js"></script>
<script>
  $("#save").on("click", function () {
    $("#saved").stop(true, true).show().delay(400).fadeOut(200);
    console.log("queued effects:", $("#saved").queue().length);
  });
</script>
result
queued effects: 2
queued effects: 2
queued effects: 2

.stop(true, true) clears any old sequence and finishes the current step, so each click starts fresh.

Key points

  • Effects on the same element wait in a queue and run one after another; chaining effects plays them in order.
  • .delay(ms) pauses the queue; it only affects queued effects, not .css() or other instant methods.
  • .stop() halts the current animation; .stop(true) also clears the queue; .finish() jumps everything to the end.
  • .promise().then(fn) runs fn once every selected element has finished all its animations.

Check yourself

1. What happens with $("#box").slideUp().slideDown()?
2. Why does $("#msg").fadeIn().delay(1000).css("color", "red") make the text red immediately?
3. A hover handler starts slideDown each time. Fast hovering makes it bounce for seconds. What fixes it?
4. What does .promise().then(fn) on ten elements wait for?