I have a javascript function that calls itself on callback, and I'm wondering how to chain other functions after all callbacks are finished? Perhaps best explained by code:
$(document).ready(function() {
sequentialFadeIn($('#demolist li'));
});
function sequentialFadeIn(item) {
item.eq(0).fadeIn("fast", function() {
(item=item.slice(1)).length && sequentialFadeIn(item)
}
}
So, the sequentialFadeIn function iterates through all list elements in 'demolist' and fades them in one after the other. What I'd like to do is perform another function (let's call it "sequentialMoveUp") after all iterations of sequentialFadeIn have been run. I'm moderately new to jQuery, so this question may well be something like 'how to chain non-jQuery methods in jQuery' or 'how to run callbacks on non-jquery methods in jQuery'...or then again it may not. Any ideas most appreciated.
You need a last callback passed to sequentialFadeIn. The next thing is to add an extra case in your conditional logic for when there's no item left. Then you call the last callback. [Demo]
Code
function sequentialFadeIn(items, callback) {
items.eq(0).fadeIn("fast", function() {
items = items.slice(1);
if (items.length) // items left
sequentialFadeIn(items, callback);
else // no items left -> finished
callback();
});
}
Usage
sequentialFadeIn($('#demolist li'), sequentialMoveUp);
Related
I've been trying to read as much as I can about javascript callbacks and jquery deferred objects but apparently things just aren't clicking for me. It seems to make a vague amount of sense when I read through it and practice examples, but when I try to apply it to my specific problem, I'm just hitting a wall. If anyone can understand what I'm trying to do and offer ideas, it would be much appreciated!
Here's some existing code:
$(document).ready(function() {
firstFunction();
secondFunction();
});
For the sake of keeping things simple here, I won't get into what firstFunction() and secondFunction() do, but suffice it to say that they both perform asynchronous work.
Here's my problem:
firstFunction() is dependent on the document being ready so needs to be inside $(document).ready(function() { }. secondFunction() isn't dependent on $(document).ready(function(), but should only execute after firstFunction has completed. I'm hoping to do all the computation for secondFunction() before the $(document).ready(function() { } block, but only execute it after firstFunction() has completed. This way firstFunction and secondFunction will execute in a more visually seamless manner. So basically, I'd like to do something like the following pseudo code:
var deferredSecondFunction = secondFunction().compute().defer(); //perform computation for secondFunction but defer execution
$(document).ready(function() {
firstFunction().done.execute(deferredSecondFunction().execute()); //finally execute secondFunction once firstFunction has completed.
});
Does anyone know if this is even possible? An important caveat is that I need to do this without the Javascript Promise object, since, for reasons outside the scope of this question, the webkit I'm working with is an old version. If anyone could help me understand this it would be appreciated!
The code shown uses a callback function and a self-invoking anonymous JavaScript function such has:
var calculatedObject;
(function(){
// Will be executed as soon as browser interprets it.
// write code here & save your calculations/operations
calculatedObject = { ... };
})();
function firstFunction(callback){
// Do stuff
callback();
}
function secondFunction(){
// Do more stuff
// Use your calculations saved in the calculated object.
}
$(document).ready(function(){
firstFunction(secondFunction);
});
This way the second function will only be called at the end of the first one.
you can use a callback..
function f1(){
//do some stuff
}
function f2(callback){
// do some async stuff
callback();
}
f2(f1);
this example passes one function to another function. the second function then calls the first whenever it's ready.
I have a functions which should run one AFTER the other, such:
function cutTomatoesAlone(Kg){
// slice my stuff
}
function cookTomatoes(Minutes){
// boil my stuff
}
I call them such:
cutTomatoesAlone(15) // 15kg, need 3 hours!
cookTomatoes(10); // need 10 minutes
But the cookTomatoes(10) finish before my cutTomatoesAlone(15).
How to run cutTomatoesAlone(15) first and when finished, then run cookTomatoes(10) ?
Edit: cutTomatoesAlone() load an external JSON. cookTomatoes(10) work on it.
Learn about promises and deferred objects. Every Ajax function in jQuery returns a promise, so you can easily chain your function calls.
For example:
function cutTomatoesAlone(Kg) {
return $.getJSON(...); // return the promise provided by $.getJSON
}
// called as
cutTomatoesAlone(15).then(function() { // attach callback
cookTomatoes(10);
});
In case of an Ajax call, the promise is resolved once the response was successfully retrieved.
You need the method The setTimeout() which will wait the specified number of milliseconds, and then execute the specified function.
function cutTomatoesAlone(Kg){
// slice my stuff
setTimeout(function() {
cookTomatoes(10)
}, delay);
}
If your functions are independent, it should work the way you expect, assuming you're not doing stuff like making http get requests asynchronously.
If you are, what you need to do is call the second function when the first one returns from its request, using JQuery's $.done() function.
Give cutTomatoesAlone a callback.
var cookingTimePerKg = 10;
function cutTomatoesAlone(Kg, Callback) {
// slice my stuff
// when done and a callback is defined do the callback
if(Callback) Callback(Kg*cookingTimePerKg);
}
Then you could do the following:
cutTomatoesAlone(15, cookTomatoes);
The callback could also be fired on the onComplete of the (potential) XHR request.
Some Function object prototype tuning would make it easier to read
Function.prototype.after = function(callback){
this();
if( typeof(callback) == "function")
callback();
}
a = function(){alert(1)};
a.after( function(){alert(2)} )
So with cooking subject:
var cutThem = function(){
cutTomatoesAlone(15) // 15kg, need 3 hours!
}
cutThem.after( function(){
cookTomatoes(10);
});
this is a proposal for general purpose, when ajax loads are on the game it's better to use their "whenDone" option to supply them a callback.
$("#basket").load("url.extension", {kilos: kg},
function(){
cookTomatoes(10);
});
I have 3 asynchronous events which can only be run one after the other. When the first asynchronous event is completed, the second is run. When the second event is completed, the third is run. My code is as shown:
asynchronousEvent1(parameters1, function () { // Do stuff
asynchronousEvent2(parameters2, function () { // Do stuff
asynchronousEvent3(parameters3, function () { // Do stuff
});
});
});
This current format means that I need to have long sequences of functions nested within another. Is there some sort of an event handler I could use, for which the format of the code would be approximately:
asynchronousEvent1(parameters1, function () { /* Do stuff */ });
whenEvent1Completed(asynchronousEvent2(parameters2, function () { /* Do stuff */ });
whenEvent2Completed(asynchronousEvent3(parameters3, function () { /* Do stuff */ });
You could use deferred objects introduced in jQuery 1.5. Assuming your functions return deferred objects, like the one returned by $.ajax (or of course you can create your own):
asynchronousEvent1(parameters1).pipe(function() {
// do stuff
return asynchronousEvent2(parameters2);
}).pipe(function() {
// do stuff
return asynchronousEvent3(parameters3);
}).then(function() {
//do stuff
});
Have a look at the last example in deferred.pipe [docs].
I don't necessarily consider this to be an answer, but more of an idea. I don't know how in real life your code is laid out, but would there be the possibility of using an array loaded with the functions to call in their order. Then, it just checks for the next one in the list, and calls that? I don't know would work, but it's an idea.
JMax
I'm not sure I entirely understand what you're looking for.
You can, for example, define a function that handles the results of Event 1, and then simply pass a reference to that function instead of writing the literal at the spot where your chain is defined. If you use that pattern, you'd probably have to tweak how parameters are passed from one event to the second.
E.g.:
function handleEvent1() {
// do something
asynchronousEvent2(parameters2, handleEvent2);
}
function handleEvent2() {
// do something
asynchronousEvent3(parameters3, handleEvent3);
}
asyncronousEvent1(parameters1, handleEvent1);
In this example, none of these event handlers benefit from the same closure as they would in your original implementation, which means you'll need to work out some data visibility stuff.
set a value to true on each event callback and put condition for firing each dependent event.
$('blah').animate({},1000,function(){animated = true}
if(animated){$('blahblah').animate()...}
Apart from making synchronous AJAX calls if you can and think it is appropriate, what is the best way to handle something like this?
var A = getDataFromServerWithAJAXCall(whatever);
var B = getDataFromServerWithAJAXCallThatDependsOnPreviousData(A);
var C = getMoreDataFromServerWithAJAXCall(whatever2);
processAllDataAndShowResult(A,B,C);
Provided that I can pass callbacks to those functions, I know I can use closures and lambdas to get the job done like this:
var A,B,C;
getDataFromServerWithAJAXCall(whatever, function(AJAXResult) {
A= AJAXResult;
getDataFromServerWithAJAXCallThatDependsOnPreviousData(A, function(AJAXResult2) {
B= AJAXResult2;
processAllDataAndShowResult(A,B,C);
});
});
getMoreDataFromServerWithAJAXCall(whatever2, function(AJAXResult) {
C= AJAXResult;
processAllDataAndShowResult(A,B,C);
});
function processAllDataAndShowResult(A,B,C) {
if(A && B && C) {
//Do stuff
}
}
But it doesn't feel right or clean enough to me. So is there a better way or at least a cleaner way to do the same thing or is it just that I'm not used to javascript functional programming?
By the way, I'm using jQuery (1.4.2) if that helps.
Thank you.
Yes, jQuery's Deferred Object is super handy.
Here's the example from the $.when() function documentation, illustrating a solution to your problem:
$.when($.ajax("/page1.php"), $.ajax("/page2.php")).done(function(a1, a2){
/* a1 and a2 are arguments resolved for the
page1 and page2 ajax requests, respectively */
var jqXHR = a1[2]; /* arguments are [ "success", statusText, jqXHR ] */
if ( /Whip It/.test(jqXHR.responseText) ) {
alert("First page has 'Whip It' somewhere.");
}
});
Cheers!
Make the callback function of each AJAX call to check/store results in a common local storage. And have another processing function that reads from this container, maybe at regular intervals or activated by each callback. This way you keep you functions clean and the focus on the Ajax call. This also keeps the accumulation scalable to n Ajax calls easy, and you dont have to modify existing code when adding a new call.
If you can use jQuery 1.5 you should be able to accomplish your needs via using the deferred object and $.when()
$.when(getDataFromServerWithAJAXCall("Call 1"), getMoreDataFromServerWithAJAXCall("Call 2")).done(function(a1, a2) {
var jqXHR = a1[2];
jqXHR.responseText;
getDataFromServerWithAJAXCallThatDependsOnPreviousData(jqXHR.responseText);
});
Simply put when the first two functions complete then it will execute the third function.
Example on jsfiddle
Use a so-called 'countdown latch'
Each of the functions have their own callback.
Have a variable called countdownlatch be upped each time a function is called and
count-down when each of the callbacks is reached (be sure to
countdown on async error as well.
Each of the callbacks separately checks to see if countdownlatch==0 if so call function
processAllDataAndShowResult
The beauty of javascript with these kind of async synchronizations is that implementing a countdownlatch is super-easy, because javascript is single-threaded, i.e: there's no way countdownlatch could get funky numbers because of racing conditions since these are non-existent (in this situation).
EDIT
Didn't see B depended on A, but the same principle applies:
var A,B,C;
var cdlatch = 2;
getDataFromServerWithAJAXCall(whatever, function(AJAXResult) {
A= AJAXResult;
getDataFromServerWithAJAXCallThatDependsOnPreviousData(A, function(AJAXResult2) {
B= AJAXResult2;
if(--cdlatch === 0){
processAllDataAndShowResult(A,B,C);
}
});
});
getMoreDataFromServerWithAJAXCall(whatever2, function(AJAXResult) {
C= AJAXResult;
if(--cdlatch === 0){
processAllDataAndShowResult(A,B,C);
}
});
function processAllDataAndShowResult(A,B,C) {
//Do stuff
}
I must admit it's not that clear as the general case I described earlier, oh well.
Let's say for example that I have two functions with random code inside and also that based on the user's system (slow, medium, or fast) there is no way to tell how long the two functions will take to complete, so the use of setTimeout is not practical when trying to fire function2 only after function1 is complete.
How can you use jQuery.deferred to make function2 fire only after function1 no matter what the time requirements are, and considering that both functions are 100% non-jQuery functions with no jQuery code inside them and therefore completely un-observable by jQuery? At the very most, the functions might include jQuery methods like .css() which do not have a time association and can run slower on old computers.
How do I assure that function2 is not executing at the same time as function1 if I call them like this:
function1(); function2();
using $.deferred? Any other answers besides those regarding $.deferred are also welcome!
ADDED March 20:
What if function1() is a lambda function where, depending on user input, the function may or may not have asynchronous calls and it is not possible to tell how many operations the function will do? It'd be a function where you wouldn't have any clue as to what would happen next, but no matter what, you'd still want function2 to execute only after everything from the lambda function (function1) is done, no matter how long it takes but as long as the asynchronous aspects are completed. How can this be achieved?
ADDED March 22:
So I guess the only way to do what I'm asking is to pass anonymous functions as callbacks to asynchromous functions that execute the callbacks after they are done, or to create event listeners that will do execute what you want when the event is finally triggered.
There's not really any way to just execute to asynchronous calls on two seperate lines and have them fire in order without manually constructing mechanisms (event handlers) within the frame containing the said functions to handle the actual execution of their actions.
A good example of these types of mechanisms would be jQuery's .queue() method and $.Defferred object.
The answers below along with reading up on jQuery's API on .queue()ing and using $.Deferred helped clarify this.
Tgr gave a great example below on how to create custom chainable functions using jQuery's $.Deferred object, and the custom functions themselves don't necessarily have to have any jQuery code inside them, which is exactly what I was looking for.
function first(deferred) {
// do stuff
deferred.resolve();
}
function second() {
// do stuff
}
$.Deferred(first).then(second);
But as Tomalak pointed out, this is unnecessary, unless you do something very tricky in first (like utilising web workers).
Update:
The basic idea is that whenever you do something that is not immediate, you create a Deferred object, and return that. (jQuery's AJAX calls already do this.) You can then use Deferred.then to delay follow-up operations.
function first() {
var deferred = $.Deferred();
var callback = function() {
deferred.resolve();
}
// do immediate stuff
someAsyncOperation(callback);
return deferred.promise(); // turns the Deferred into a Promise, which
// means that resolve() will not be accessible
}
function second() {
// do stuff
}
first().then(second); // or: $.when(first).then(second)
If second is also an asynchronous operation, you can use $.when's merging capabilities:
function second() {
var anotherDeferred = $.Deferred();
// do stuff with anotherDeferred
return anotherDeferred.promise();
}
$.when(first(), second()).then(third); // third will run at the moment when
// both first and second are done
JavaScript itself is not asynchronous. It is single-threaded, synchronous.
function1();
function2();
will execute one after another unless they contain asynchronous calls. In that case, there will always be a callback you can pass (like onSuccess for XmlHttpRequest). Place the second function there.
To say the truth, they strictly execute one after another even if they contain asynchronous bits. It's just that the asynchronous bits might not yet be finished when the rest of the function is.
EDIT Your jsFiddle example, fixed (see it):
function foo() {
$('#foo')
.html('<span>foo1</span>')
.animate(
{ /* properties */
left: '100px'
},
360, /* duration */
'swing', /* easing */
function () { /* "complete" callback */
$('#foo').append('<span>foo2</span>');
bar();
}
);
}
As I said. There will always be a callback you can pass.