understanding how $.promise works similar to transitionEnd - javascript

I was just messing around on the documentation page of jQuery.promise() and came across the following peice of code :
$("button").on("click", function () {
$("p").append("Started...");
$("div").each(function (i) {
$(this).fadeIn().fadeOut(1000 * (i + 1));
});
$("div").promise().done(function () {
$("p").append(" Finished! ");
});
});
FIDDLE HERE
Now I do understand that $.defer in jQuery assists in Asynchronous programming, also I understand that $.done and $.fail are part of the $promise object .
I have read an interesting article HERE. There are a few good examples of how $.defer can be used to monitor css-3 transitions.
However in the fiddle example I provide, I fail to understand how $.promise picks up the fact that the transition is complete. How does promise pick up that that fadeout() is complete?
How does the below piece of code really work?
$("div").promise().done(function () {
$("p").append(" Finished! ");
});
How is promise really working here? Can anyone explain?

To put it simply, jQuery creates a queue of Deferred objects on each object returned by the $("div") selector (these are visible using the .data() function).
When you add some CSS animations to the divs with jQuery functions such as fadeIn() or fadeOut(), it creates Deferred objects that are appended to each individual div queues.
Using$("div").promise().done() on the parent collection allows to check if all of the children Deferred object queues are empty (jQuery will iterate on the children elements).

I haven't delved into the jQuery source, but here's my understanding.
$.promise returns a Promise which completes once all actions of a certain type have ended. By default, the 'type' is fx (source).
When the fx queue is empty, the promise will resolve.
In your fiddle, you call fadeIn(), which adds the animation to the fx queue. ($.fadeIn() has queue: true by default.) $.fadeOut does the same.
When the queue is empty, the promise will resolve. This fiddle would support that. (Queue is 'inprogress' whilst the animations are running, but empty 100ms later.)
A slightly more convoluted fiddle - notice how the promise completes when we clear the fx queue using $(el).queue('fx',[]);?

Related

How to remove an attribute after animation in jquery

I'd like to remove an id from an image after its animation is completed. I have this in my code:
if(index == 1 && direction =='down'){
$('#slidetext1 #rock').animate({right:"0"});
$('#slidetext1 #deer').animate({left: "0"}).addClass('open').removeAttr('id');
}
It's not working because it removes the id before even starting the animation, but what I want to do is to remove the id #deer from the image and add ('open') after the .animate() has been executed.
so here i made a jsfiddle: http://jsfiddle.net/67oe1jvn/45/ . pay attection to the left image as you scroll down under the HELLO h1. the thing i want to achive is: when i get to the second section, i'd like to see both of the images slide in the view with the directive "transition:all 1.2s ease-out;" AND whenever the section gets changed make them slide out of the view with a faster transiction, so it won't been noticed that much.
You need to supply the complete callback function which is fired once the animation is complete. You can achieve this by updating the code to following
if(index == 1 && direction =='down'){
$('#slidetext1 #rock').animate({right:"0"});
$('#slidetext1 #deer').animate({left: "0"}, function(){
$(this).addClass('open').removeAttr('id')
});
}
For reference - http://api.jquery.com/animate/
Please try this
$.when($('#slidetext1 #deer').animate({left: "0"})).then(function(){
$('#slidetext1 #deer').addClass('open').removeAttr('id')
});
If a single Deferred is passed to jQuery.when(), its Promise object (a subset of the Deferred methods) is returned by the method. Additional methods of the Promise object can be called to attach callbacks, such as deferred.then. When the Deferred is resolved or rejected, usually by the code that created the Deferred originally, the appropriate callbacks will be called.
OR
$('#slidetext1 #deer').animate({left: "0"}).promise().done(function(){
$('#slidetext1 #deer').addClass('open').removeAttr('id')
});
The .promise() method returns a dynamically generated Promise that is resolved once all actions of a certain type bound to the collection, queued or not, have ended.
DEMO with .promise()
DEMO with .when()

create jquery infinite animation not using recursion?

How to use jquery to create infinite animation,
BUT NOT using recursion way?
The recursion solution I found: jQuery .animate() callback infinite loop
The problem of using recursion is:
while animating, the browser tab of currrent page will take MORE and MORE memory.
You have actually made a mistake in assuming that the code is "recursive". It is not recursive. Your comment that "the browser tab of current page will take MORE and MORE memory." is not because of any recursion in that code. If you have a memory leak per second (as per your comments), then the problem lays elsewhere.
The instances of the code actually run sequentially and not via nested stack calls (i.e. linear and not recursive).
Using the most basic example you linked to:
function start() {
$('#element').animate({}, 5000, 'linear', start);
}
start();
here is what actually happens:
A function called start is defined and then called once
Inside start, an animation operation is started, then start immediately exits!
The animate just adds information to a single animation queue for the element, then returns.
The animation queue is processed step by step by a separate process (single timer etc).
When the queued entry has met its expected end state, it calls the callback - The global start function stored in the queued entry.
That simple adds another entry on the animation queue and exits.
Basically the code looks recursive, but as the operations are async, via a queue, it is not recursive. I don't know if there is an official name for these, so I just call them chained callbacks.
UPDATED WITH TEST RESULTS
Conclusion... All methods burn up the same amount of memory and it eventually gets released, doesn't keep building forever so not really an issue.
Example 1
This is the code the OP originally had a problem with memory usage increasing in Chrome.
(function($){
$(function(){ //document.ready
function start() {
$('#animate').animate({'margin-left':'150px'}, 1000, function () {
$(this).animate({'margin-left':'50px'}, 1000, 'linear', start);
});
}
start();
});
})(jQuery);
Example 2
Current solution including a callback as requested by Bergi to avoid potential "drifting" in setinterval.
(function($){
$(function(){ //document.ready
});
(function customSwipe(element) {
element
.animate({"margin-left" : "150px"}, 1000)
.animate({"margin-left" : "50px"}, 1000, function(){
setTimeout(function(){
customSwipe(element);
}, 2000);
});
})($('#animate'));
})(jQuery);
Example 3
Original Answer I gave, using setInterval()
(function($){
$(function(){ //document.ready
setInterval(function(){
$("#animate").animate({'margin-left':'150px'},1000);
$("#animate").animate({'margin-left':'50px'},1000);
},2000);
});
})(jQuery);
Skeleton w/ Jquery
Empty page w/ only the #animate element
(function($){
$(function(){ //document.ready
});
})(jQuery);
DATA AFTER TAB OPEN FOR 10 MINUTES
CODE STARTING ENDED
Example 1 14.300k 19.996k
Example 2 14.300k 20.020k
Example 3 14.300k 20.344k
Skeleton w/ jQuery 14.300k 15.868k
Interesting that the code that did nothing still increased usage slightly. These values go up and down as memory is used and released. Another thing would be to use the "purge memory" button in task manager to see how much of that used memory is garbage waiting to be collected.
DATA AFTER 25 MINUTES
Example 1 14.300k 18.640k
Example 2 14.300k 18.724k
Example 3 14.300k 18.876k
Skeleton w/ jQuery 14.300k 15.868k
Conclusion... All methods burn up the same amount of memory and it eventually gets released, doesn't keep building forever so not really an issue.
So just using the most solid, sound code would be the best option, Example 2 would be my choice.
UPDATED USING CALLBACK
Have you tried using setTimeout with $.animate()?
(function($){
(function customSwipe(element) {
element
.animate({'margin-left':'150px'}, 1000)
.animate({'margin-left':'50px'}, 1000, function(){
setTimeout(function(){
customSwipe(element);
}, 2000);
});
})($('#animate'));
$(function(){ //document.ready
});
})(jQuery);
Remove the setTimeout() if you don't need it to delay between animations.
JSFIDDLE of the above code working...
You might also want to look into this for more intricate animations.
http://api.jquery.com/jquery.fx.interval/
Conclusion... All methods burn up the same amount of memory and it eventually gets released, doesn't keep building forever so not really an issue.
What you are seeing in the Chrome TM is every time it fires the animation, that much memory is requested and the OS "commits" that memory to chrome for the operation. Once the operation is completed, the memory is still committed. At some point Chromes garbage collector comes along and releases that memory and your usage stats will drop back down. So you will see the memory going up and down if you watch it long enough.
You can put --purge-memory-button at the end of your Chrome command line to have a Purge Memory button available in Chrome TM. This might help you to see how much memory is actually waiting to be released.
Hope this answer helps you and maybe some others.

Fire a callback only once on animating two objects

I was starting to make a quite complex animation with jQuery and I was looking for a certain way to animate elements together. I wrote the code somewhat as follows, which I could use to animate multiple elements together:
Here's the fiddle: http://jsfiddle.net/wkhrU/
$(document).ready(function(){
$('#firstElement, #secondElement').animate(
{left : '+=400px'},
500,
"linear",
function(){
alert("completed");
});
});
However, the callback function fires twice i.e. each time for the element inside. What I was looking for is to be something like animating multiple elements simaltaneously for the same time duration and after the completion, fire a call inside which I wanted to swap the id attrib of the elements. I can't do the same here because on completion callback, it swaps the ids once and on the next function callback, swaps them back again. What's the appropriate way to achieve this?
SOLUTION
You could maybe use a promise() to call the function to be executed instead of the inbuilt callback of animate. This ensures that the function in done is called only after animating both the elements. It's basically like you're asking #firstElement & #secondElement to promise JS that it'll inform when both of 'em are done so that you could attach a done handler to it.
$(document).ready(function () {
$('#firstElement, #secondElement').animate({
left: '+=400px'
},
500, "linear").promise().done(function () {
alert("completed");
});
});
DEMO
http://jsfiddle.net/hungerpain/wkhrU/1/
MORE INFO ON USED METHODS :
promise
Docs : http://api.jquery.com/promise/
What it does : Makes sure that all actions of a certain type bound to the collection, queued or not, have finished.
done
Docs : http://api.jquery.com/deferred.done/
What it does : Add handlers to be called when the promise is resolved.

Creating an animation queue with parameters

I'm trying to leverage the jquery queues in order to execute a series of functions in order. These "queued" functions, actually animate objects around on a page.
this was my original code for building my queue:
this.queue = function(fx, params) {
this.animation_queue.push({fx: fx, params: params});
};
Then, once all functions are queued, I consume it something like this:
this.animate = function() {
for(var i=0; i<this.animation_queue.length; i++) {
this.animation_queue[i].fx.apply(this, [this.animation_queue[i].params]);
}};
The problem, obviously, that I ran into is that the queue elements were not executing properly. Though they did execute sequentially, because each function performed an animation, I actually need each queue elements to execute after the previous element's animation is finished.
I looked in to using jquery queues, like so:
this.queue = function(fx, params) {
this.animation_queue.delay(500, 'animations');
this.animation_queue.queue('animations', function(next) {
next();
});
};
But I'm not sure how I can call my functions as I did before, with the parameters.
Any suggestions about how I can accomplish this?
According to the doc:
http://api.jquery.com/queue/
You needn't worry about building and managing your own queue, the queue is managed by jQuery the moment you call queue on the element.
Here is a fiddle of their example that works just fine:
http://jsfiddle.net/7GxwR/
So in order to feed your animations onto the queue, just create a single function that will stack all the animations on to the element in order and then call .queue(...) on the element to force it to execute in a queue.
If you're wanting to dynamically change / build the queue you would need to stop all animations, build the runIt function in the fiddle using a loop over an array of dynamic JSON objects that stores your animation parameters and timings, then call showIt and runIt as shown in the fiddle and example in the jQuery API.
The basic point being here, don't build your own queue / array, jQuery will do this for you if you call queue on an element with multiple effects applied already.
Hope this helps.

how to set style through javascript in IE immediately

recently I've encountered a problem with IE. I have a function
function() {
ShowProgress();
DoSomeWork();
HideProgress();
}
where ShowProgress and HideProgress just manipulate the 'display' CSS style using jQuery's css() method.
In FF everything is OK, and at the same time I change the display property to block, progress-bar appears. But not in IE. In IE the style is applied, once I leave the function. Which means it's never shown, because at the end of the function I simply hide it. (if I remove the HideProgress line, the progress-bar appears right after finishing executing the function (more precisely, immediately when the calling functions ends - and so there's nothing else going on in IE)).
Has anybody encountered this behavior? Is there a way to get IE to apply the style immediately?
I've prepared a solution but it would take me some time to implement it. My DoSomeWork() method is doing some AJAX calls, and these are right now synchronous. I assume that making them asynchronous will kind of solve the problem, but I have to redesign the code a bit, so finding a solution just for applying the style immediately would much simplier.
Thanks rezna
Synchronous requests block the entire UI, which is horrible. You are right in your assumption, but if you want to continue down this path to the inner circles of $hell, try this:
function () {
ShowProgress();
window.setTimeout(function () {
DoSomeWork();
HideProgress();
}, 0);
}
Note that I have not tested this. Try playing with the time-out value (currently 0) if you still do not see anything.
Try throwing the HideProgress method into a setTimeout. For example:
function() {
ShowProgress();
DoSomeWork();
setTimeout(HideProgress,0);
}
Even though the delay is supposedly 0 milliseconds, this will have the net effect of throwing the HideProcess method to the end of the queue and may give the browser the breathing room it needs.
[Edit] I should have mentioned that this method does have a drawback if you invoke this method very often and rapidly: a race condition. You could end up with a previous timeout executing while another DoSomeWork() is executing. This will happen if DoSomeWork take a very long time to finish. If this is a risk, you may want to implement a counter for your progress bar and only execute HideProgress if the counter that started it is the same as the counter's present value.

Categories