I want to say when this function close() is finished run this function init(). But it's not working for me.
$.when(close(toolTip)).done(init(toolTip, anchor));
I am not using the $.when for anything ajax related, just trying to make sure close() is finished before I call init(), and no I can't stick init() at the end of close(). Any ideas?
ok here is close()
var close = function (toolTip) {
toolTip.fadeOut('fast', function (e) {
if (typeof e !== 'undefined') {
//Re-set values applied when initted
var toolTipBd = toolTip.find('.bd:first');
toolTip.css('width', '');
toolTipBd.css('max-height', '');
toolTip.css('max-height', '');
toolTipBd.css('overflowY', '');
}
});
};
No where in close() can it call init().
Your close() implementation should be like this:
var close = function (toolTip) {
var d = $.Deferred();
toolTip.fadeOut('fast', function (e) {
if (typeof e !== 'undefined') {
//Re-set values applied when initted
var toolTipBd = toolTip.find('.bd:first');
toolTip.css('width', '');
toolTipBd.css('max-height', '');
toolTip.css('max-height', '');
toolTipBd.css('overflowY', '');
}
d.resolve();
});
return d.promise();
};
$.when works with Deferred's. It returns a new Deferred which will resolve when all the Deferred's you provided resolve.
As close() doesn't seem to be returning a Promise, when will resolve straight away (per the docs for when().
However, if close() is synchronous, you don't need when() at all. If it is asynchronous, you need to return a Promise, and resolve it when your animation or whatever has completed;
function close(what) {
var promise = jQuery.Deferred();
what.fadeOut('slow', function () {
promise.resolve();
});
return promise.promise();
}
... but you still don't need $.when as only 1 promise is involved. $.when is only useful when multiple promises are at play.
close(toolTip).done(function () {
init(toolTip, anchor);
});
Note also that done(init(tooltip, anchor)) will call init immediately, and pass the result of that function invocation to done(); instead, you need to pass a function to done. As init needs parameters, we've fixed this by introducing an anonymous function. If init didn't need any parameters, it'd have been as simple as:
close(toolTip).done(init);
Simply return toolTip:
return toolTip.fadeOut(...
using the callback to resolve a deferred object can result in odd results if there are more than one elements selected for whatever reason.
This works because jQuery objects have a .promise method that when called, return a promise object that resolves when all active animations are completed. $.when calls .promise on all passed in arguments.
You'll also need to call init differently, for example,
$.when(close(toolTip)).done(function(){
init(toolTip, anchor);
});
And, as pointed out by others, you could then shorten that to
close(toolTip).promise().done(function(){
init(toolTip, anchor);
});
Related
I am not asking what is the appropriate syntax for chaining, I know it could be something like:
$('myDiv').removeClass('off').addClass('on');
As far as I know chaining is one of the advantages against other famous frameworks. Can someone explain to me how chaining works here?
If you have an object with certain methods, if each method returns an object with methods, you can simply call a method from the object returned.
var obj = { // every method returns obj---------v
first: function() { alert('first'); return obj; },
second: function() { alert('second'); return obj; },
third: function() { alert('third'); return obj; }
}
obj.first().second().third();
DEMO: http://jsfiddle.net/5kkCh/
All that it is doing is returning a reference to this when the method finishes. Take this simple object for example:
var sampleObj = function()
{
};
sampleObj.prototype.Foo = function()
{
return this;
};
You could chain these calls all day because you return a reference to this:
var obj = new sampleObj();
obj.Foo().Foo().Foo().Foo() // and so on
jQuery simply performs an operation, then returns this.
Basically the first function call $('myDiv') returns a jQuery object, then each subsequent call returns the same one.
Loosely,
var $ = function(selector) {
return new jQuery(selector);
};
jQuery.prototype.removeClass = function(className) {
// magic
return this;
}
return $this;
each jQuery function returns an instance of the jQuery class, which can then have methods called on it. you could break it down, and this code would have the same effect.
jQuery_obj = $('myDiv');
jQuery_obj = jQuery_obj.removeClass('off');
jQuery_obj = jQuery_obj.addClass('on');
The point is that a function must evaluate to the "parent" function. So e.g.
foo().bar().test();
has to evaluate to:
foo().test();
so that you can call another function on foo(). To do this, you can return this:
function foo() {
// empty, nothing interesting here
}
foo.prototype.bar = function() {
return this;
}
foo.prototype.test = function() {
return this;
}
Then,
var something = new foo();
something.bar() === something; // true
And because of this:
something.bar().test() === something.test(); // true
So because something.bar() evaluates to something, you can immediately call the second function in one go.
In chaining parent function/method returns an object which is then used by the child function/method, and things go on such a way. In short the jQuery or $ returns itself (an object) which allows the chaining.
It is the same mechanism below
var obj=$('input'); //returns jQuery object
var obj1=obj.val('a'); //returns jQuery object
var obj2=obj1.fadeOut();//returns jQuery object
It looks like this if it is done with chaining
$('input').val('a').fadeOut();
Here is an example of conditional callback chaining, like is used on the $.ajax jQuery function.
// conditional callback function example
myFunction = function () {
// define event callback prototypes without function parameter
var callback_f = new Object;
callback_f.callback1 = function () { return callback_f; };
callback_f.callback2 = function () { return callback_f; };
if ([condition]){
// redefine the callback with function parameter
// so it will run the user code passed in
callback_f.ReturnPlayer = function (f) { f(); return callback_f; };
}else{
callback_f.NewPlayer = function (f) { f(); return callback_f; };
}
return callback_f;
}
One of the way to chaining, check demo .
test("element").f1().f2().f3()
Chaining is used to connect multiple events and functions in a selector.
To run multiple jQuery commands, one after the other, on the same element(s). Generally chaining uses the jQuery built in functions that makes compilation a bit faster.
It makes your code short and easy to manage and it gives better performance,
Eaxample
Without Chaining
$(document).ready(function(){
$('#dvContent').addClass('dummy');
$('#dvContent').css('color', 'red');
$('#dvContent').fadeIn('slow');
});
With Chaining
$(document).ready(function(){
$('#dvContent').addClass('dummy')
.css('color', 'red')
.fadeIn('slow');
});
Note: The chain starts from left to right. So left most will be called first and so on.
Chaining allows us to run multiple jQuery methods (on the same element) within a single statement.
The following example chains together the css(), slideUp(), and slideDown() methods. The "p1" element first changes to red, then it slides up, and then it slides down :
$("#p1").css("color", "red").slideUp(2000).slideDown(2000);
I'm writing a test using Selenium and JavaScript. I'm new to both, and also new to functional programming and promises. I'm trying to create a function that needs to do 3 things:
Click on an input
Clear the input
SendKeys to input
My current function does not work:
var clearAndSendKeys = function(driver, elementIdentifier, sendKeys) {
var returnValue;
driver.findElement(elementIdentifier).then(function(inputField){
inputField.click().then(function() {
inputField.clear().then(function() {
returnValue = inputField.sendKeys(sendKeys);
});
});
});
return returnValue;
}
The function would then be called as for example:
clearAndSendKeys(driver, webdriver.By.id('date_field'), '14.09.2015').then(function(){
//Do stuff
});
I expected the variable returnValue to contain the promise from sendKeys. However the function clearAndSendKeys returns the undefined variable before sendKeys is ran. I assume this is because returnValue was never defined as a promise, and so the program does not know that it needs to wait for sendKeys.
How can I make my function clearAndSendKeys return the promise from sendKeys? I'd rather avoid having to add a callback to the clearAndSendKeys function.
Edit: Removed .then({return data}) from the code as this was a typo.
You have to return each promise from the .then callback:
var clearAndSendKeys = function(driver, elementIdentifier, sendKeys) {
return driver.findElement(elementIdentifier).then(function(inputField){
return inputField.click().then(function() {
return inputField.clear().then(function() {
return inputField.sendKeys(sendKeys);
});
});
});
}
The promise returned by .then will resolve to the same value as the value returned from the callback.
See Why is my variable unaltered after I modify it inside of a function? - Asynchronous code reference for why your current code does not work. Promises are asynchronous.
First of all its probably not the best idea to nest promises, completely defeating their main purpose of eliminating callback hell. then callback can return Thenable object that allows to create nice chains of async operations.
In this case you just need to store reference to input field available as a result of the first async operation in the scope of the main function and then create chain of async operations that can be returned from this function.
var clearAndSendKeys = function(driver, elementIdentifier, sendKeys) {
var inputFieldRef;
return driver.findElement(elementIdentifier)
.then(function(inputField){
inputFieldRef = inputField;
return inputField.click();
}).then(function() {
return inputFieldRef.clear();
}).then(function() {
return inputFieldRef.sendKeys(sendKeys);
});
}
Let's say I have multiple functions func1, func2, func3, etc.....
And they all contain an AJAX/async function within them:
function funcX(){
// some ajax request
}
If in a main function I am calling func1, func2, func3 sequentially like so:
$(document).ready(function(){
func1();
func2();
func3();
...
}
Will each ajax/async function's call be certain to execute in the order of their parent functions? At first I thought they might be, but the behavior of my program seems to be suggesting otherwise...
If not, is there a good (hopefully simple?) alternative to having a long chain of callbacks?
Will each ajax/async function's call be certain to execute in the order of their parent functions?
They should execute in order, but their internal callbacks can be called in any order.
If not, is there a good (hopefully simple?) alternative to having a long chain of callbacks?
You could use a promise, and execute the next function when the promise has been resolved.
This example uses jQuery...
var fn1 = function () {
var d = $.Deferred();
setTimeout(function () {
$("body").text("Callback 1 done.") && d.resolve();
}, Math.random() * 1300 + 800);
return d.promise();
};
var fn2 = function () {
var d = $.Deferred();
setTimeout(function () {
$("body").text("Callback 2 done.") && d.resolve();
}, 500);
return d.promise();
};
$.when(fn1(), fn2()).then(function () {
setTimeout(function () {
$("body").text("All done.");
}, 300);
});
jsFiddle.
We use $.when() and pass the invoked functions we want to execute to it. We then use then() to show a final message (I placed a setTimeout() here so you can see the last resolved function's message in the document).
Each of these functions have their own deferred object which return the promise. A setTimeout() mocks an XHR for example's sake. When this callback is executed, we resolve the deferred object.
Once both have been deferred, we reach the callback for then().
To serialize tasks, I've written a helper function, which can also be found in my earlier answer:
function serializeTasks(arr, fn, done)
{
var current = 0;
fn(function iterate() {
if (++current < arr.length) {
fn(iterate, arr[current]);
} else {
done();
}
}, arr[current]);
}
It takes an array of values (in your case those are actually functions), a loop function and a completion handler. Below is the loop function:
function loopFn(nextTask, fn)
{
fn(nextTask);
}
It accepts an intermediate completion function as the first argument and each element of the aforementioned array.
To set everything in motion:
serializeTasks([func1, func2, func3], loopFn, function() {
console.log('all done');
});
Your functions are called with a single argument which should be passed to the AJAX success callback, e.g.
func1(nextTask)
{
$.ajax({
...,
success: nextTask
});
}
The order in which the asynch results are returned is not deterministic, and may wary every time.
func2 might complete before func1 etc
It is important to ensure correct order of execution. One pattern is to call the next function in the success callback of the prior function
Ex:
$.get("/someUrl",function(){
$.get("/nextAjaxCall", function(){
.....
});
});
If the dependency chain is very simple, I don't think it's necessary to introduce a framework to handle this
Or look at async library and it's awesomeness !
async
I have a bunch of nested functions returning deferred objects from ajax calls. Here's what my code looks like
function makeCalls() {
var ajaxDfd1 = $.ajax(...);
ajaxDfd1.then(function() {
// want to execute after first call
var ajaxDfd2 = $.ajax(...);
ajaxDfd2.done(function() {
// want to execute after second call
});
return ajaxDfd2;
});
return ajaxDfd1;
}
makeCalls().done(function() {
// stuff here was executed early
});
But my calls are not being executed in the order I intend them to be. Stuff inside makeCalls().done() seems to be called before ajaxDfd2 is actually done.
Choc,
You've made an incorrect assumption in your answer; namely that $.ajax() returns a "regular object".
This is not correct.
$.ajax() returns a jqXHR object, which is a superset of a Promise, ie. it has all of Promise's methods plus others which are specific to AJAX (such as .abort()).
If the object returned by $.ajax() was not Promise-compatible, then the code in your question would throw an error on trying to execute makeCalls().done(...).
The reason why your .done() function executes first it that the function returns the outer jqXHR.
Here's a couple of ways to achieve the behaviour you were expecting :
function makeCalls() {
var dfrd = new Deferred();
$.ajax(...).then(function() {
$.ajax(...).done(dfrd.resolve);
});
return dfrd.promise();
}
makeCalls().done(function(data) {
// ...
});
or
function makeCalls(fn) {
$.ajax(...).then(function() {
$.ajax(...).done(fn);
});
}
makeCalls(function(data) {
// ...
});
You could even try :
function makeCalls() {
return $.ajax(...).then(function() {
return $.ajax(...);
});
}
makeCalls().done(function() {
// ...
});
I knew I must be getting something wrong, and spent a really long time staring at this:
When should I use jQuery deferred's "then" method and when should I use the "pipe" method?
Later I realised that I can't do
var dfdObj = $.ajax(...);
dfdObj.then(...);
return dfdObj;
This doesn't actually return the deferred object. It returns an Ajax object, and since it is a regular object, it is automatically classified as resolved and thus the .done() function was being called early. Instead, I need to do this if I want to return a var
var dfdObj = $.ajax(...).then(...);
return dfdObj;
Your code works like this:
function makeCalls() {
var ajax1 = $.ajax(…);
var ajax1and2 = ajax1.then(makeCall2);
function makeCall2() {
// want to execute after first call
var ajax2 = $.ajax(…);
ajax2.done(after2);
function after2() {
// want to execute after second call
});
return ajax2;
});
return ajax1;
}
var ajax1 = makeCalls();
ajax1.done(function() {
// stuff here was executed early
});
ajax1and2 is the result of the then/pipe call you wanted. A callback on it will wait for the result of makeCall2 (i.e. the ajax2) which waits for ajax1. It would be equal to the after2 callback.
Yet, your makeCalls function returns the ajax1 object (you can see I assigned it to a global ajax1 variable as well) and both the makeCall2 and the done-callback will be executed after your first ajax has finished.
So, instead you need to return the ajax1and2 deferred:
function makeCalls() {
return $.ajax(…).then(function() {
return $.ajax(…).done(function() {
// want to execute after second call
});
});
}
makeCalls().done(function() {
// stuff here is now executed after second call as well
});
I am calling a method that has some logic in a deferred, and when that logic finishes, I want to return the value to the callee. See below:
//Callee.js
var myAssistant = new Assistant();
console.log(myAssistant.whatIsTheValue());
//Assistant.js
whatIsTheValue : function(someArg) {
var deferred = someService.getSomething();
deferred.then(lang.hitch(this, this._getTheValue));
//In theory, I want to return whatever this._getTheValue returns, how can I do that?!
}
_getTheValue() {
...
...
return xyz;
}
Deferreds are asynchronous operations. Therefore you can't return a variable in a normal way from them because they will not execute until after the current function context completes.
If you want to do more with that value you will need to do so in terms of another callback (IE chaining the then statements.)
The point of deferreds is to provide sequential operations for callbacks. So you can chain them to achieve the results you want. If you need the results to be available in your current execution context, you're going to have to find a synchronous (not deferreds) method of doing what you want.
So something like this
//Assistant.js
whatIsTheValue : function(someArg) {
var deferred = someService.getSomething();
var next = deferred.then(lang.hitch(this, this._getTheValue));
next.then(/*insert next function here*/);
}
You need to understand that using a deferred lang.hitch isn't going to execute until after whatistheValue is done operating. So instead of returning the value to whatever function called whatisthevalue, you're going to have to put the logic of processing that value into a new function and use that as an additional callback for your deferred. This will probably require some restructuring of your program.
I do not know what your lang.hitch does, but the solution should look like that:
Assistant.prototype.whatIsTheValue = function(someArg) {
var deferred = someService.getSomething();
return deferred.then(lang.hitch(this, this._getTheValue));
// ^^^^^^
};
var myAssistant = new Assistant();
myAssistant.whatIsTheValue().then(console.log); // use console.log.bind(console) in Chrome
// ^^^^ - it is a promise you return
use JQuery's $when instead.
Example
// assuming both getData and getLocation return their respective Promise
var combinedPromise = $.when(getData(), getLocation())
// function will be called when both getData and getLocation resolve
combinePromise.done(function(data,location){
alert("We got data: " + dataResult + " and location: " + location);
});
http://www.html5rocks.com/en/tutorials/async/deferred/