0

See this fiddle: http://jsfiddle.net/ym1aLk25/9/

var s = $('#shake');
var randomTran = function (flag) {
    flag = flag || 0;
    if (flag < 6) {
        var rh = Math.floor((Math.random() * 10) - 5),
            rv = Math.floor((Math.random() * 10) - 5);
        s.transit({x: rh,y: rv}, 50, randomTran.bind(this, ++flag))
    };
};
randomTran();
s.transit({x: 0,y: 0});

I'm trying to make the element shake for a few seconds and then return to its original position. But it doesn't work as I expected, the problem is with the callback function. According to this question: If a jQuery function calls itself in its completion callback, is that a recursive danger to the stack?, it seems that while callback is still looping, the codes that come after the callback function are also being executed. So how to achieve my goal, other than setting a timeout? And where can I find a more detailed explanation about this mechanism?

Community
  • 1
  • 1
shenkwen
  • 3,536
  • 5
  • 45
  • 85

2 Answers2

1

Your fiddle is working as expected, and the animations are queued (they even would be queued if .transit() was called multiple times repeatedly, as the plugin uses jQuery's internal animation queue). The only thing is that 50 milliseconds for a animation on up to 5 pixels are much too fast. I've increased the time and printed the counter in this revision of your fiddle.

it seems that while callback is still looping, the codes that come after the callback function are also being executed.

There is no "looping callback". The callback is passed to the function which returns before the callback is called - and the code that called .transit() continues (which, in your case, is the closing } brace from the if, the end of the randomTran() call, and the s.transit({x: 0,y: 0}); initialisation.

Once this code has finished executing, other code can be executed - asynchronously. The callback that was stored somewhere is now - in the future, 50ms after the transit() call - being called; does start another transition, does schedule another callback, and ends.

Bergi
  • 630,263
  • 148
  • 957
  • 1,375
  • Thank you for the explanation, so to make `s.transit(0,0)` excutes only after the other transitions, one way is to identify the queue and push `s.transit(0,0)` into it, correct? And I also tried this:http://jsfiddle.net/ym1aLk25/18/, but the behavior is like a mystery to me... – shenkwen Oct 13 '14 at 06:43
  • Either that, but as the queue is actually filled asynchronously (after each single animation, from the callback) that would be complicated. Or you just execute `s.transit(0, 0)` in the `else` part of the `if`, i.e. when your `flag` counter has reached the limit. – Bergi Oct 13 '14 at 10:13
  • I tried to execute it in else, but it just didn't work out as I would think. See this fiddle: http://jsfiddle.net/ym1aLk25/19/ – shenkwen Oct 13 '14 at 14:26
  • You had for some reason a semicolon `;` after your `if`-statement, which caused a syntax error on your `else`. [Fixed here](http://jsfiddle.net/ym1aLk25/20/) – Bergi Oct 13 '14 at 14:35
0

There is no callback in your randomTran function, so it runs asynchronously. This means that randomtran() starts, and just after, s.transit({x: 0,y: 0}); is executed.

You could run s.transit({x: 0,y: 0}); in the callback of randomTran to have it execute after the rest like so :

var s = $('#shake');
var randomTran = function (flag,callback) {
    flag = flag || 0;
    if (flag < 6) {
        var rh = Math.floor((Math.random() * 10) - 5),
            rv = Math.floor((Math.random() * 10) - 5);
        s.transit({x: rh,y: rv}, 50, randomTran.bind(this, ++flag))
    }else{
      callback();
    }
};
randomTran(0,function(){ //0 is added because randomTran expects flag as first param
  s.transit({x: 0,y: 0}); //this will be executed after randomtran calls back, eg when flag=6
});

but... I'm afraid you're overcomplicating things and I'm not really sure what you want to achieve.

xShirase
  • 11,975
  • 4
  • 53
  • 85
  • What I want to achieve is like this fiddle: http://jsfiddle.net/ym1aLk25/16/ After briefly shaking, the element returns to its original position. I knew this solution but I just want to better understand concepts like 'callback','asynchronously callback' and how this 'recursive callback' works. Your codes above don't work as I anticipate, see http://jsfiddle.net/ym1aLk25/17/, after shaking, the element won't return. And I also tried this:http://jsfiddle.net/ym1aLk25/18/, but the behavior makes me totally lost my mind... – shenkwen Oct 13 '14 at 06:22