JamesYin
JamesYin

Reputation: 742

How can I run a lot of functions in sequence?

Maybe this question is duplicate. I found a lot of similar questions, but no one can actually solve my problem. This is my task:

function animate(){
  $ul.each(function(){
     $(this).find('li').each(function(){
          //animate block
          $(this).animate({top:'-100%'},100,function(){
             $(this).css({top:'100%'});
          });
          //endblock
        });
     });
}

As you may know, the 'animate block' functions will run at a same time. I want them to run in sequence. How can I achieve that?

I have read jQuery 'deferred', 'Q' relate article, but still confusing.

Sorry for my English.

---------addition------

If I want to run animate function several times, what should I do?

Upvotes: 0

Views: 151

Answers (4)

guest271314
guest271314

Reputation: 1

Try utilizing .queue() , $.map() , .delay() ; substituting window.innerHeight for 100 , px unit for % unit at .animate() ; substituting .animate for .css at complete callback

var ul = $("ul");
ul.queue("_fx", $.map(ul.find("li"), function(el, i) {
  return function(next) {
    return $(el).animate({
      top: "-" + window.innerHeight / 10 + "px"
    }, 100, function() {       
      $(this).delay(100).animate({
        top: window.innerHeight - $(this).height() * 1.5 + "px"
      }, 100, next);
    })
  };
})).dequeue("_fx");
body {
  height: 200px;
}
ul li {
  top: 150px;
  position: relative;
  width: 50px;
  height: 50px;
  background: red;
  padding 8px;
  display: inline-block;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js">
</script>
<ul>
  <li>a
  </li>
  <li>b
  </li>
  <li>c
  </li>
  <li>d
  </li>
  <li>e
  </li>
  <li>f
  </li>
  <li>g
  </li>
</ul>

Upvotes: 0

Roamer-1888
Roamer-1888

Reputation: 19288

If you want to avoid jQuery's awkward .queue()/.dequeue(), then you can build a promise chain from a jQuery collection of your <li> elements.

function animate($ul) {
    var p = $.when(); //resolved starter promise
    $("li", $ul).each(function(i, li) {
        p = p.then(function() {
            return $(li).animate({ top:'-100%' }, 100, function() {
                return $(li).css({ top:'100%' });
            }).promise();
        });
    });
    return p;
}

As the chain settles, the animations will occur in sequence.

More elegantly, you can extend jQuery to have a .reduce() method, adopted from Array.prototype.

jQuery.fn.reduce = Array.prototype.reduce;

Then, build the required promise chain by invoking .reduce() on the jQuery collection.

function animate($ul) {
    return $("li", $ul).reduce(function(p, li) {//wow, jQuery has a .reduce() method!
        return p.then(function() {
            //Here, perform the required animation and return a promise of its completion.
            return $(li).animate({top:'-100%'}, 100, function() {
                $(li).css({top:'100%'});
            }).promise();
        });
    }, $.when());//Resolved starter promise, ie p in the first iteration of .reduce() .
}

This pattern is well worth learning as it has many applications.

In both examples, the outermost return allows you to chain .then() wherever animate() is called :

animate($("ul")).then(function() {
    //do something when all the <li> animations have completed.
});

Upvotes: 0

freakish
freakish

Reputation: 56527

Something like this should work:

var last_deferred = $.Deferred();
last_deferred.resolve();

$ul.each(function(){
    $(this).find('li').each(function(){
         var deferred = $.Deferred(),
             $that = $(this);
         last_deferred.then(function() {
             $that.animate({top:'-100%'},100,function(){
                $(this).css({top:'100%'});
                deferred.resolve();
             });
         });
         last_deferred = deferred;
    });
});

Explanation: here I use 2 important features of deferred objects .then and .resolve. When you call .resolve(); the deferred object will fire all handlers assigned to it via .then. So what happens is that I first define "dummy" deferred which I immediately resolve. If deferred object is resolved then adding a handler via .then will automatically make it run. So what happens is this

  1. last_deferred is defined and resolved
  2. Temporary deferred is created
  3. Add .then handler to last_deferred
  4. .then handler is fired and after some things it does it resolves deferred (thus triggers .then on deferred)
  5. last_deferred is redefined to be deferred
  6. go back to 2.

So this is kind of an asynchronous loop (because .animate is an asynchronous operation). In each step you say "when previous deferred is resolved tell it to resolve the next one".

Upvotes: 0

kapantzak
kapantzak

Reputation: 11750

Check this solution using queue() function.

This function waits until the animation completes and then executes it's contents.

var anim = function(i) {
  $('.test').eq(i).animate({'top':'20px'}, 350).queue(function() {
    i++;
    anim(i);
    $(this).dequeue();
  });
}
anim(0);
.test {
  width:50px;
  height:50px;
  float: left;
  margin-right:10px;
  background-color:green;
  position:relative;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<div class="test">test</div>
<div class="test">test</div>
<div class="test">test</div>
<div class="test">test</div>
<div class="test">test</div>
   

Upvotes: 1

Related Questions