Wait till a Function with animations is finished until running another Function
JavascriptJqueryCallbackJavascript Problem Overview
I'm having an issue with normal (non-ajax) functions that involve lots of animations within each of them. Currently I simply have a setTimeout
between functions, but this isn't perfect since no browsers / computers are the same.
Additional Note: They both have separate animations/etc that collide.
I can't simply put one in the callback function of another
// multiple dom animations / etc
FunctionOne();
// What I -was- doing to wait till running the next function filled
// with animations, etc
setTimeout(function () {
FunctionTwo(); // other dom animations (some triggering on previous ones)
}, 1000);
Is there anyway in js/jQuery to have:
// Pseudo-code
-do FunctionOne()
-when finished :: run -> FunctionTwo()
I know about $.when()
& $.done()
, but those are for AJAX...
- MY UPDATED SOLUTION
jQuery has an exposed variable (that for some reason isn't listed anywhere in the jQuery docs) called $.timers, which holds the array of animations currently taking place.
function animationsTest (callback) {
// Test if ANY/ALL page animations are currently active
var testAnimationInterval = setInterval(function () {
if (! $.timers.length) { // any page animations finished
clearInterval(testAnimationInterval);
callback();
}
}, 25);
};
Basic useage:
// run some function with animations etc
functionWithAnimations();
animationsTest(function () { // <-- this will run once all the above animations are finished
// your callback (things to do after all animations are done)
runNextAnimations();
});
Javascript Solutions
Solution 1 - Javascript
You can use jQuery's $.Deferred
var FunctionOne = function () {
// create a deferred object
var r = $.Deferred();
// do whatever you want (e.g. ajax/animations other asyc tasks)
setTimeout(function () {
// and call `resolve` on the deferred object, once you're done
r.resolve();
}, 2500);
// return the deferred object
return r;
};
// define FunctionTwo as needed
var FunctionTwo = function () {
console.log('FunctionTwo');
};
// call FunctionOne and use the `done` method
// with `FunctionTwo` as it's parameter
FunctionOne().done(FunctionTwo);
you could also pack multiple deferreds together:
var FunctionOne = function () {
var
a = $.Deferred(),
b = $.Deferred();
// some fake asyc task
setTimeout(function () {
console.log('a done');
a.resolve();
}, Math.random() * 4000);
// some other fake asyc task
setTimeout(function () {
console.log('b done');
b.resolve();
}, Math.random() * 4000);
return $.Deferred(function (def) {
$.when(a, b).done(function () {
def.resolve();
});
});
};
Solution 2 - Javascript
add the following to the end of the first function
return $.Deferred().resolve();
call both functions like so
functionOne().done(functionTwo);
Solution 3 - Javascript
Along with Yoshi's answer, I have found another very simple (callback type) solution for animations.
jQuery has an exposed variable (that for some reason isn't listed anywhere in the jQuery docs) called $.timers, which holds the array of animations currently taking place.
function animationsTest (callback) {
// Test if ANY/ALL page animations are currently active
var testAnimationInterval = setInterval(function () {
if (! $.timers.length) { // any page animations finished
clearInterval(testAnimationInterval);
callback();
}
}, 25);
};
Basic useage:
functionOne(); // one with animations
animationsTest(functionTwo);
Hope this helps some people out!
Solution 4 - Javascript
promises
, a JavaScript feature of the ECMAScript 6
standard. If your target platform does not support promises
, polyfill it with PromiseJs.
This answer uses You can get the Deferred
object jQuery creates for the animation using .promise()
on the animation call. Wrapping these Deferreds
into ES6 Promises
results in much cleaner code than using timers.
You can also use Deferreds
directly, but this is generally discouraged because they do not follow the Promises/A+ specification.
The resulting code would look like this:
var p1 = Promise.resolve($('#Content').animate({ opacity: 0.5 }, { duration: 500, queue: false }).promise());
var p2 = Promise.resolve($('#Content').animate({ marginLeft: "-100px" }, { duration: 2000, queue: false }).promise());
Promise.all([p1, p2]).then(function () {
return $('#Content').animate({ width: 0 }, { duration: 500, queue: false }).promise();
});
Note that the function in Promise.all()
returns the promise. This is where magic happens. If in a then
call a promise is returned, the next then
call will wait for that promise to be resolved before executing.
jQuery uses an animation queue for each element. So animations on the same element are executed synchronously. In this case you wouldn't have to use promises at all!
I have disabled the jQuery animation queue to demonstrate how it would work with promises.
Promise.all()
takes an array of promises and creates a new Promise
that finishes after all promises in the array finished.
Promise.race()
also takes an array of promises, but finishes as soon as the first Promise
finished.
Solution 5 - Javascript
Is this what you mean man: http://jsfiddle.net/LF75a/
You will have one function fire the next function and so on, i.e. add another function call and then add your functionONe
at the bottom of it.
Please lemme know if I missed anything, hope it fits the cause :)
Code:
function hulk()
{
// do some stuff...
}
function simpsons()
{
// do some stuff...
hulk();
}
function thor()
{
// do some stuff...
simpsons();
}
Solution 6 - Javascript
ECMAScript 6 UPDATE
This uses a new feature of JavaScript called Promises
functionOne().then(functionTwo);
Solution 7 - Javascript
You can do it via callback function.
$('a.button').click(function(){
if (condition == 'true'){
function1(someVariable, function() {
function2(someOtherVariable);
});
}
else {
doThis(someVariable);
}
});
function function1(param, callback) { ...do stuff callback(); }
Solution 8 - Javascript
Here is a solution for n-calls (recursive function). https://jsfiddle.net/mathew11/5f3mu0f4/7/
function myFunction(array){
var r = $.Deferred();
if(array.length == 0){
r.resolve();
return r;
}
var element = array.shift();
// async task
timer = setTimeout(function(){
$("a").text($("a").text()+ " " + element);
var resolving = function(){
r.resolve();
}
myFunction(array).done(resolving);
}, 500);
return r;
}
//Starting the function
var myArray = ["Hi", "that's", "just", "a", "test"];
var alerting = function (){window.alert("finished!")};
myFunction(myArray).done(alerting);
Solution 9 - Javascript
You can use the javascript Promise
and async/await
to implement a synchronized call of the functions.
Suppose you want to execute n
number of functions in a synchronized manner that are stored in an array, here is my solution for that.
async function executeActionQueue(funArray) {
var length = funArray.length;
for(var i = 0; i < length; i++) {
await executeFun(funArray[i]);
}
};
function executeFun(fun) {
return new Promise((resolve, reject) => {
// Execute required function here
fun()
.then((data) => {
// do required with data
resolve(true);
})
.catch((error) => {
// handle error
resolve(true);
});
})
};
executeActionQueue(funArray);