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()...}
Related
I am new to javascript, I have gone through tutorials about callbacks, but I don't see any that answers this, both method below offers the same results, the only difference I see is callback allows dynamically passing in a callback function.
Are there other advantages, I am missing?
Thank you.
Callback
function logOne(callback) {
setTimeout(() => {
console.log("one");
callback();
}, 1000);
}
function logTwo() {
console.log("two");
}
logOne(logTwo); // one, two
No Callback
function logOne() {
setTimeout(() => {
console.log("one");
logTwo();
}, 1000);
}
function logTwo() {
console.log("two");
}
logOne(); // one, two
Your first example is more flexible: You can use any callback, not just logTwo.
Both have their uses, it depends on whether you need the flexibility or whether logOne should be specifically tied to logTwo.
Callback of function: If you want to do some operation on some event
Like show time on click of a button. Then you override onclick
function for that button. So whenever (independent on time) that
button is clicked, that application internal framework will call
onclick event and your onclick function will be called.
Normal function : Every function is normal function
Calling a function is when you actually do the function.
Passing a function is when function A needs function B in order to
work. So when you call function A, you pass it function B as an
argument. In this case you are not calling function B, instead you are
providing it to function A so that function A can call it.
Your second example create a tight coupling between logOne and logTwo functions. This way you end up with a logOne function that can't be reused since it only works with one exact function.
By passing a callback, you make your function more flexible and generalized. Now it is able to work with a wide range of other functions as long as they have the same "shape" - same number of arguments in a same order.
I am new to next(), done() etc. and am struggling with propagating parameters between serial executions/chaining of possibly otherwise asynchronous functions.
I want to force serial execution of 2 functions so they can be called with something like either:
f1('#{arg1a}', '#{arg1b}').done(
f2('#{arg2a}', '#{arg2b}')
);
OR something like:
f1('#{arg1a}', '#{arg1b}', '#{arg2a}', '#{arg2b}').done(f2);
Where the arg values passed in are gleaned from query parameters using JSF.
Note that:
arg2a and arg2b are in my case completely unrelated to arg1a and arg1b, and the invocation of f2 does NOT depend in any way on what happens in f1, other than it must execute strictly afterwards, even if f1 is otherwise usually asynchronous.
I am not defining on-the-fly anonymous functions or such inside done() here (yet), I want to be able to call a library-defined function with some known params.
In this example, the functions would be something like:
function f1(arg1a, arg1b) {
//do something with arg1a, arg1b
return $.Deferred().resolve();
}
function f2(arg2a, arg2b) {
// Do something with arg2a and arg2b AFTER f1 has fully run.
}
OR something like:
function f1(arg1a, arg1b, arg2a, arg2b) {
//do something with arg1a, arg1b
// Somehow ensure f1 is finished then execute f2(arg2a, arg2b)
}
function f2(arg2a, arg2b) {
// Do something with arg2a and arg2b AFTER f1 has fully run.
}
Just using callback chaining did not work for the situation I am tackling. See also: How link to and target/open a p:tab within an p:accordionPanel within a p:tab within a p:tabview
An acceptable answer MUST permit me to have a pre-defined function f2 with pre-defined parameters
You need to pass parameters to .resolve(), then use .then()
function f1(arg1a, arg1b) {
return $.Deferred(function(dfd) {
//do something with arg1a, arg1b
// you can alternatively call `.resolve()` without passing parameters
// when you are finished doing something with `arg1a`, `arg1b`,
// which should call chained `.then()` where `f2` is called
dfd.resolve(arg1a, arg1b)
}).promise();
}
function f2(arg2a, arg2b) {
// Do something with arg2a and arg2b AFTER f1 has fully run.
}
f1(arg1, arg2)
.then(function() {
// call `f2` here
f2('#{arg2a}', '#{arg2b}');
})
// handle errors
.catch(function(err) { // alternatively use `.fail()`
console.log(err)
});
jsfiddle https://jsfiddle.net/wuy8pj8d/
You've almost got it right except you've forgotten to wrap the code you want to execute in the future (when done is eventually called) inside a function:
f1('#{arg1a}', '#{arg1b}').done(function(){
f2('#{arg2a}', '#{arg2b}')
});
This also works with regular callbacks. For example, say you've defined f1 to accept a callback instead of a promise, you'd then do:
f1('#{arg1a}', '#{arg1b}',function(){
f2('#{arg2a}', '#{arg2b}')
});
Nothing special here. There's no separate syntax for forcing callbacks to accept custom arguments, just wrap it in another function.
This also works for variables thanks to closures:
var a='#{arg1a}', b='#{arg1b}';
var c='#{arg2a}', d='#{arg2b}';
f1(a,b).done(function(){
f2(c,d)
});
The variables c and d will be accessible within done().
in jQuery, I iterate over an xml list of areas and do a POST request to get detailed information about each area. Because sending thousands of requests at once is debilitating for the client and server, I would like to set a flag so that I wait for a request to finish before sending the subsequent [next] request.
if the xml looks like this:
<area>5717</area>
<area>5287</area>
<area>5376</area>
then the xml parsing kinda looks like:
$(xml).find("area").each( function() {
doPost();
}
and the doPost() function looks like
doPost : function () {
$.post( ... )
}
Basically, I would like to add a toggling "wait" but I'm not sure how to achieve this. Is there a way I can keep the essential ".each" iteration or is another type of loop better for this?
Thanks in advance.
A general algorithm off the top of my head:
You could put the whole list into an array. Take the first item of the array and post it. In the success handler of your post you could recursively call the function with the next index int the list.
I wouldn't use async: false because it would then be a blocking operation, which I assume the OP doesn't want.
You can use:
$.ajaxSetup({async:false});
at the top of your script to make your AJAX calls synchronous.
Alternately, you can replace $.post() with $.ajax() and set the async flag to false.
can you do a setTimeout ? that will allow for the function to still process asynchronous and allow for you to wait for some time in there too.
http://www.w3schools.com/js/js_timing.asp
setTimeout(function() {}, 5000)
You can refactor your doPost() function to take the <area> element to process as an argument, and chain into the next element from your success callback. Something like:
(function doPost($area) {
if ($area.length > 0) {
$.post({
// your options,
success: function() {
// your success handling...
doPost($area.next("area"));
}
});
}
})($(xml).find("area").first());
EDIT: Maybe the code above was a little too compact indeed.
Basically, the aim is to refactor your function so that it takes a jQuery object containing the next <area> element to process, or nothing if processing should stop:
function doPost($area) {
if ($area.length > 0) {
// Perform POST request and call ourselves from success callback
// with next <area> element (or nothing if there's no such element).
}
}
Then call this function with the first <area> element to process:
doPost($(xml).find("area").first());
The first code fragment in my answer does both at the same time. Functions are first-class objects in Javascript, and you can call a function you've just defined by enclosing its definition with parenthesis and providing the usual argument list, also surrounded by parenthesis.
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.
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);