Using JQuery Deferred and Promise - javascript

I have a function that I want to use deferred and promise to chain an animation.
The first animation is a type writer plugin, using https://github.com/stephband/jticker/blob/master/js/jquery.jticker.js.
The second is a function that contains other animations.
What I want to do is run the first animation and when the animation is completed, run the second.
$(function () {
var ticker =setTimeout(runTicker(), 8000);
$.when(ticker).done(function(){
setTimeout(Other(),16000)});
});
function runTicker() {
$("#ticker").ticker({
cursorList: " ",
rate: 50,
delay: 18000
}).trigger("play").trigger("stop");
}
I have tried numerous examples of deferred, but still can't get it.
I finally cleared all the examples in order to get the ticker working again.
How would I use deferred and promise to run the Other() function?
Thank you

Don't know how to solve your actual problem with a proper callback-based solution (not enough information on the Ticker plugin you use), but I can explain what goes wrong in your current code:
var ticker = setTimeout(runTicker(), 8000);
Don't call runTicker immediately. What you want is to pass the function itself - not the result of its invocation - into setTimeout. A [plain integer] number will be returned and is assigned to ticker. It can be used for identifying the timeout when aborting it via clearTimeout - and nowhere else.
$.when(ticker)...
creates a new Deferred now. Have a look at its documentation: It will combine Deferred objects with each other and create immediately-resolved Promises for any other values - like numbers. Therefore, your done callback is also called immidiately, and again you make the mistake with executing Other instead of passing it into setTimeout.
As the plugin you used seems very limited in regard to callbacks, I've written my own now (just for fun :-). It adapts my solution from this former answer which uses pure DOM methods quite elegantly. It is written as a standard jQuery plugin, even favours stop and go methods and - most important - integrates well in the jQuery fx queue. That means you will be able to use it exactly like animate() regarding callbacks and chaining, and if you want to work with Deferreds you can invoke the promise() method to get a Promise for the queue's end. Example call:
$('#ticker').fadeOut().delay(2000).typewriter({framerate:1000/30}, function() {
// the typewriter effect has just ended
}). ... .promise().done(function() {
// effect queue has ended - start Other()
});
jQuery(".stop").click($.fn.typewriter.bind($("#ticker"), "stop"));
→ Code at jsfiddle.net

setTimeout obviously will not return a jQuery Deferred object, its a native Javascript method here. You need to re-write it like so:
function runTicker() {
return jQuery.Deferred(function( promise ) {
setTimeout(function() {
$("#ticker").ticker({
cursorList: " ",
rate: 50,
delay: 18000
}).trigger("play").trigger("stop");
promise.resolve();
}, 8000);
}).promise();
}
And then you can call it like
var ticker = runTicker();
jQuery.when( ticker ).done(function() {
setTimeout(Other,16000)});
});

Related

Perform operation after all the call back functions got executed

I have the following scenerio
asyncFunction(x1)
asyncFunction(x2)
asyncFunction(x3)
...
...
asyncFunction(xn)
The function of the asyncFunction is to get data and image from the server and append to the body of the html. The problem is the data sizes vary each time so each function may complete at different time. This makes it useless as some top elements would be still loading even after the bottom elements are loaded.
How can I write the Javascript code in such a way that the functions data is appended after all the callbacks have been completed or alternatively execute the function i only after function j has been executed, where j < i.
I am using Javascript and Ajax for getting the data.
PS: This is almost similar to a news feed.
You can use jquery deffered objects
In every function define deffered object, for example:
var asyncFunction1 = function(x1) {
var d = new $.Deferred();
...
//when it will be ready
d.resolve();
...
return d.promise();
}
And then
$.when(asyncFunction1(), asyncFunction2(), ...).done(function() {
// here all functions will be finished
});
Use can do this easily with using promises. Inform yourself about the topic with articles like this. Then I would suggest you have a look at the Q framework.
What you are looking for would then be that you want to execute code only after all promises have been resolved. This would look like this if you use Q:
Q.all([a,b,c,d]).then(function(){
//runs after all of the promises fulfilled
});

Critical Section in JavaScript or jQuery

I have a webpage, in which a certain Ajax event is triggered asynchronously. This Ajax section could be called once or more than once. I do not have control over the number of times this event is triggered, nor the timing.
Also, there is a certain code in that Ajax section that should run as a critical section, meaning, when it is running, no other copy of that code should be running.
Here is a pseudo code:
Run JavaScript or jQuery code
Enter critical section that is Ajax (when a certain process is waiting for a response callback, then do not enter this section again, until this process is done)
Run more JavaScript or jQuery code
My question is, how can I run step 2 the way described above? How do I create/guarantee a mutual exclusion section using JavaScript or jQuery.
I understand the theory (semaphores, locks, ...etc.), but I could not implement a solution using either JavaScript or jQuery.
EDIT
In case you are suggesting a Boolean variable to get into the critical section, this would not work, and the lines below will explain why.
the code for a critical section would be as follows (using the Boolean variable suggestions):
load_data_from_database = function () {
// Load data from the database. Only load data if we almost reach the end of the page
if ( jQuery(window).scrollTop() >= jQuery(document).height() - jQuery(window).height() - 300) {
// Enter critical section
if (window.lock == false) {
// Lock the critical section
window.lock = true;
// Make Ajax call
jQuery.ajax({
type: 'post',
dataType: 'json',
url: path/to/script.php,
data: {
action: 'action_load_posts'
},
success: function (response) {
// First do some stuff when we get a response
// Then we unlock the critical section
window.lock = false;
}
});
// End of critical section
}
}
};
// The jQuery ready function (start code here)
jQuery(document).ready(function() {
var window.lock = false; // This is a global lock variable
jQuery(window).on('scroll', load_data_from_database);
});
Now this is the code for the lock section as suggested using a Boolean variable. This would not work as suggested below:
The user scrolls down, (and based on the association jQuery(window).on('scroll', load_data_from_database); more than one scroll event is triggered.
Assume two scroll events are triggered right at almost the same moment
Both call the load_data_from_database function
The first event checks if window.lock is false (answer is true, so if statement is correct)
The second event checks if window.lock is false (answer is true, so if statement is correct)
The first event enters the if statement
The second event enters the if statement
The first statement sets window.lock to true
The second statement sets window.lock to true
The first statement runs the Ajax critical section
The second statement runs the Ajax critical section.
Both finish the code
As you notice, both events are triggered almost at the same time, and both enter the critical section. So a lock is not possible.
I think the most helpful information you provided above was your analysis of the locking.
The user scrolls down, (and based on the association jQuery(window).on('scroll', load_data_from_database); more than one
scroll event is triggered.
Assume two scroll events are triggered right at almost the same moment
Both call the load_data_from_database function
The first event checks if window.lock is false (answer is true, so if statement is correct)
The second event checks if window.lock is false (answer is true, so if statement is correct)
Right away this tells me that you have come to a common (and quite intuitive) misunderstanding.
Javascript is asynchronous, but asynchronous code is not the same thing as concurrent code. As far as I understand, "asynchronous" means that a function's subroutines aren't necessarily explored in depth-first order as we would expect in synchronous code. Some function calls (the ones you are calling "ajax") will be put in a queue and executed later. This can lead to some confusing code, but nothing is as confusing as thinking that your async code is running concurrently. "Concurrency" (as you know) is when statements from different functions can interleave with one another.
Solutions like locks and semaphores are not the right way to think about async code. Promises are the right way. This is the stuff that makes programming on the web fun and cool.
I'm no promise guru, but here is a working fiddle that (I think) demonstrates a fix.
load_data_from_database = function () {
// Load data from the database. Only load data if we almost reach the end of the page
if ( jQuery(window).scrollTop() >= jQuery(document).height() - jQuery(window).height() - 300) {
console.log(promise.state());
if (promise.state() !== "pending") {
promise = jQuery.ajax({
type: 'post',
url: '/echo/json/',
data: {
json: { name: "BOB" },
delay: Math.random() * 10
},
success: function (response) {
console.log("DONE");
}
});
}
}
};
var promise = new $.Deferred().resolve();
// The jQuery ready function (start code here)
jQuery(document).ready(function() {
jQuery(window).on('scroll', load_data_from_database);
});
I'm using a global promise to ensure that the ajax part of your event handler is only called once. If you scroll up and down in the fiddle, you will see that while the ajax request is processing, new requests won't be made. Once the ajax request is finished, new requests can be made again. With any luck, this is the behaviour you were looking for.
However, there is a pretty important caveats to my answer: jQuery's implementation of promises is notoriously broken. This isn't just something that people say to sound smart, it is actually pretty important. I would suggest using a different promise library and mixing it with jQuery. This is especially important if you are just starting to learn about promises.
EDIT: On a personal note, I was recently in the same boat as you. As little as 3 months ago, I thought that some event handlers I was using were interleaving. I was stupefied and unbelieving when people started to tell me that javascript is single-threaded. What helped me is understanding what happens when an event is fired.
In syncronous coding, we are used to the idea of a "stack" of "frames" each representing the context of a function. In javascript, and other asynchronous programming environments, the stack is augmented by a queue. When you trigger an event in your code, or use an asynchronous request like that $.ajax call, you push an event to this queue. The event will be handled the next time that the stack is clear. So for example, if you have this code:
function () {
this.on("bob", function () { console.log("hello"); })
this.do_some_work();
this.trigger("bob");
this.do_more_work();
}
The two functions do_some_work and do_more_work will fire one after the other, immediately. Then the function will end and the event you enqueued will start a new function call, (on the stack) and "hello" will appear in the console. Things get more complicated if you trigger an event in your handler, or if you trigger and event in a subroutine.
This is all well and good, but where things start to get really crappy is when you want to handle an exception. The moment you enter asynchronous land, you leave behind the beautiful oath of "a function shall return or throw". If you are in an event handler, and you throw an exception, where will it be caught? This,
function () {
try {
$.get("stuff", function (data) {
// uh, now call that other API
$.get("more-stuff", function (data) {
// hope that worked...
};
});
} catch (e) {
console.log("pardon me?");
}
}
won't save you now. Promises allow you to take back this ancient and powerful oath by giving you a way to chain your callbacks together and control where and when they return. So with a nice promises API (not jQuery) you chain those callbacks in a way that lets you bubble exceptions in the way you expect, and to control the order of execution. This, in my understanding, is the beauty and magic of promises.
Someone stop me if I'm totally off.
I would recommend a queue which only allows one item to be running at a time. This will require some modification (though not much) to your critical function:
function critical(arg1, arg2, completedCallback) {
$.ajax({
....
success: function(){
// normal stuff here.
....
// at the end, call the completed callback
completedCallback();
}
});
}
var queue = [];
function queueCriticalCalls(arg1, arg2) {
// this could be done abstractly to create a decorator pattern
queue.push([arg1, arg2, queueCompleteCallback]);
// if there's only one in the queue, we need to start it
if (queue.length === 1) {
critical.apply(null, queue[0]);
}
// this is only called by the critical function when one completes
function queueCompleteCallback() {
// clean up the call that just completed
queue.splice(0, 1);
// if there are any calls waiting, start the next one
if (queue.length !== 0) {
critical.apply(null, queue[0]);
}
}
}
UPDATE: Alternative solution using jQuery's Promise (requires jQuery 1.8+)
function critical(arg1, arg2) {
return $.ajax({
....
});
}
// initialize the queue with an already completed promise so the
// first call will proceed immediately
var queuedUpdates = $.when(true);
function queueCritical(arg1, arg2) {
// update the promise variable to the result of the new promise
queuedUpdates = queuedUpdates.then(function() {
// this returns the promise for the new AJAX call
return critical(arg1, arg2);
});
}
Yup, the Promise of cleaner code was realized. :)
You can wrap the critical section in a function and then swap the function so it does nothing after first run:
// this function does nothing
function noop() {};
function critical() {
critical = noop; // swap the functions
//do your thing
}
Inspired by user #I Hate Lazy Function in javascript that can be called only once

Wait for multiple deferred to complete [duplicate]

So, I have a page that loads and through jquery.get makes several requests to populate drop downs with their values.
$(function() {
LoadCategories($('#Category'));
LoadPositions($('#Position'));
LoadDepartments($('#Department'));
LoadContact();
};
It then calls LoadContact(); Which does another call, and when it returns it populates all the fields on the form. The problem is that often, the dropdowns aren't all populated, and thus, it can't set them to the correct value.
What I need to be able to do, is somehow have LoadContact only execute once the other methods are complete and callbacks done executing.
But, I don't want to have to put a bunch of flags in the end of the drop down population callbacks, that I then check, and have to have a recursive setTimeout call checking, prior to calling LoadContact();
Is there something in jQuery that allows me to say, "Execute this, when all of these are done."?
More Info
I am thinking something along these lines
$().executeAfter(
function () { // When these are done
LoadCategories($('#Category'));
LoadPositions($('#Position'));
LoadDepartments($('#Department'));
},
LoadContact // Do this
);
...it would need to keep track of the ajax calls that happen during the execution of the methods, and when they are all complete, call LoadContact;
If I knew how to intercept ajax that are being made in that function, I could probably write a jQuery extension to do this.
My Solution
;(function($) {
$.fn.executeAfter = function(methods, callback) {
var stack = [];
var trackAjaxSend = function(event, XMLHttpRequest, ajaxOptions) {
var url = ajaxOptions.url;
stack.push(url);
}
var trackAjaxComplete = function(event, XMLHttpRequest, ajaxOptions) {
var url = ajaxOptions.url;
var index = jQuery.inArray(url, stack);
if (index >= 0) {
stack.splice(index, 1);
}
if (stack.length == 0) {
callback();
$this.unbind("ajaxComplete");
}
}
var $this = $(this);
$this.ajaxSend(trackAjaxSend)
$this.ajaxComplete(trackAjaxComplete)
methods();
$this.unbind("ajaxSend");
};
})(jQuery);
This binds to the ajaxSend event while the methods are being called and keeps a list of urls (need a better unique id though) that are called. It then unbinds from ajaxSend so only the requests we care about are tracked. It also binds to ajaxComplete and removes items from the stack as they return. When the stack reaches zero, it executes our callback, and unbinds the ajaxComplete event.
You can use .ajaxStop() like this:
$(function() {
$(document).ajaxStop(function() {
$(this).unbind("ajaxStop"); //prevent running again when other calls finish
LoadContact();
});
LoadCategories($('#Category'));
LoadPositions($('#Position'));
LoadDepartments($('#Department'));
});
This will run when all current requests are finished then unbind itself so it doesn't run if future ajax calls in the page execute. Also, make sure to put it before your ajax calls, so it gets bound early enough, it's more important with .ajaxStart(), but best practice to do it with both.
Expanding on Tom Lianza's answer, $.when() is now a much better way to accomplish this than using .ajaxStop().
The only caveat is that you need to be sure the asynchronous methods you need to wait on return a Deferred object. Luckily jQuery ajax calls already do this by default. So to implement the scenario from the question, the methods that need to be waited on would look something like this:
function LoadCategories(argument){
var deferred = $.ajax({
// ajax setup
}).then(function(response){
// optional callback to handle this response
});
return deferred;
}
Then to call LoadContact() after all three ajax calls have returned and optionally executed their own individual callbacks:
// setting variables to emphasize that the functions must return deferred objects
var deferred1 = LoadCategories($('#Category'));
var deferred2 = LoadPositions($('#Position'));
var deferred3 = LoadDepartments($('#Department'));
$.when(deferred1, deferred2, deferred3).then(LoadContact);
If you're on Jquery 1.5 or later, I suspect the Deferred object is your best bet:
http://api.jquery.com/category/deferred-object/
The helper method, when, is also quite nice:
http://api.jquery.com/jQuery.when/
But, I don't want to have to put a bunch of flags in the end of the drop down population callbacks, that I then check, and have to have a recursive setTimeout call checking, prior to calling LoadContact();
No need for setTimeout. You just check in each callback that all three lists are populated (or better setup a counter, increase it in each callback and wait till it's equal to 3) and then call LoadContact from callback. Seems pretty easy to me.
ajaxStop approach might work to, I'm just not very familiar with it.

Canceling a Deferred Promise in jQuery

How can I cancel a promise without removing the element from the DOM?
fiddle
I ran this code:
$("#box")
.delay(2000)
.show("slow")
.delay(2000)
.promise()
.then(function(){log("Done");});
After this, is there a way to cancel the promise? Both clearQueue() and stop(true) didn't work, because it's not an animation that I'm trying to cancel. I saw that remove() should do it ... but I only want to stop the promise, not remove the entire element.
Good news. Since yesterday you can cancel your promise.
I published the new version of my small plugin jquery-timing that provides two methods amongst many others called .wait() and .unwait().
var deferred = $("#box").delay(2000).show("slow").delay(2000).promise();
$.wait(deferred, function(){ log("Done"); });
If you then want to unregister the callback:
$.unwait();
These static versions of wait and unwait also support an optional group name to not cancel any handler but only a specific set.
Besides that you can do a lot more smart stuff like:
$('#box').wait(deferred).addClass('ready');
or the whole code in one chain, without unwait option:
$("#box").delay(2000).show("slow")
.delay(2000).join(function(){log("Done");})).addClass('ready');
or the same even shorter with option to cancel the two pauses:
$("#box").wait(2000).show("slow",$)
.wait(2000, function(){log("Done");})).addClass('ready');
Just see the docs, examples, and API what fits best for you.
I believe you can use $('#box').remove();
From the jQuery documentation here: http://api.jquery.com/promise/
The returned Promise is linked to a Deferred object stored on the .data() for an element. Since the .remove() method removes the element's data as well as the element itself, it will prevent any of the element's unresolved Promises from resolving. If it is necessary to remove an element from the DOM before its Promise is resolved, use .detach() instead and follow with .removeData() after resolution."
I don't suppose you'd want something like http://jsfiddle.net/2cq8M/ ? I'm involving two promises (one just to handle the case at the end of the set of animations, the other to resolve or reject as needed).
You want to use a deferred in this case instead of a promise, however, you can use the promise of the animation to resolve the deferred.
http://jsfiddle.net/LG9eZ/9/
var stopDone = true;
function log(msg) {
$(".debug").append(new Date() + " - " + msg + "<br/>");
}
log("Starting");
var boxAnimation = new $.Deferred();
boxAnimation.done(function() {
log("Done");
});
boxAnimation.fail(function() {
log("Stopped");
});
$("#box").delay(2000).show("slow").delay(2000).promise().then(function() {
boxAnimation.resolve(); // when all the animations are done, resolve the deferred.
});
if (stopDone)
{
boxAnimation.reject();
}
As a side note, deferreds can only rejected or resolved once. Once they are rejected or resolved, you cannot change their state.

Double delay in jQuery or JavaScript

Is there a way to delay the calling of a JavaScript function until two criteria are met?
I have a slideUp() animation and a .get() function that start at the same time, but either one could finish first. I want a function to be called when both have completed.
You just have to keep track, either a counter or (my preferred approach) flags for all relevant information.
var slideDone, getDone;
$("#foo").slideUp(function() {
slideDone = true;
nextThing();
});
$.get({
// ...
success: function() {
getDone = true;
nextThing();
}
});
function nextThing() {
if (slideDone && getDone) {
// Do the next thing
}
}
Now, obviously my two conditions above are very artificial, but I find that most of the time when this comes up in my real work, I have actual information I can use for the check and so don't resort to artificial conditions.
Or a counter:
var counter = 0;
++counter;
$("#foo").slideUp(function() {
nextThing();
});
++counter;
$.get({
// ...
success: function() {
nextThing();
}
});
function nextThing() {
if (--counter === 0) {
// Do the next thing
}
}
To someone used to multi-threaded programming, that looks like a race condition (what if the slideUp completes before we start the get?), but it's not one in JavaScript on browsers, which is single-threaded (barring the use of web workers, which use a syntax that wouldn't apply to the above anyway).
If you find this coming up a lot, you could of course always isolate the logic (not that there's a lot of it, but...) into an object and reuse it.
You could create a simple boolean variable which both delegate methods have acesss to and check whether the previous method has executed.
This way you can check in your delegate functions if the previous function has completed and execute some method.
T.J. Crowders answer includes some good example code of this approach.
You can use jQuery's Deferred Object:
http://api.jquery.com/category/deferred-object/
It's actually perfect for what you need. You can set a function to only execute when all the deferred objects bound to it get resolved. Some of jQuery's native functions return a deferred object by default such as the $.ajax and I believe animations as well (the latest version of jQuery at least)
Read the docs, it'll do a much better job at explaining it than I could.

Categories

Resources