I'm trying to get some data in multiple functions and would like to chain them in order to execute the last function only when all data was properly loaded.
The problem is that the functions in the .done() part will be called instantly and don't wait until the Deferred-Object is resolved. I also tried it by chaining them with .then() but this also didn't work.
var array1, array2;
function doStuffWithReceivedData() {
// Working with the data
}
function getArray1() {
var defArray1 = $.Deferred();
$.getJSON(url, function(data) {
if (data.success) {
array1 = data.payload;
defArray1.resolve();
} else {
// Information displayed that there was an error
}
})
return defArray1;
}
// Function 2 is like the first one
function getArray2() {...};
$(document).read(function() {
getArray1()
.done(getArray2()
.done(doStuffWithReceivedData()));
}
The argument to .done() must be a function. You're calling the function, not passing it. Take off the parentheses.
$(document).ready(function() {
getArray1()
.done(function() {
getArray2().done(doStuffWithReceivedData));
}
}
Related
This works:
function getDataJSON() {
var queryString = "https://www.examplesite.com/someJSON.json";
$.getJSON(queryString, function(data) {
doStuffWithData(data)
});
}
function doStuffWithData(JSON) {
// some code that refers to JSON
}
getDataJSON();
But this complains that a variable (JSON) is undefined somewhere in doStuffWithData():
function getDataJSON(callback) {
// Gets share data and runs callback when received
var queryString = "https://www.examplesite.com/someJSON.json";
$.getJSON(queryString, function(data) {
if(typeof callback === "function") {
callback(data);
}
});
}
function doStuffWithData(JSON) {
// some code that refers to JSON
}
getDataJSON(doStuffWithData());
What am I likely to be doing wrong? The $.getJSON() call takes a second or two so I do need to wait for that and do stuff after it. I think the issue is with the code execution order, but it could be that I've misunderstood how to properly pass the data to the callback function.
It would be better if I could just load the data into a variable all my other functions can access.
This:
getDataJSON(doStuffWithData());
should be this:
getDataJSON(doStuffWithData);
Otherwise it invoked that function immediately and attempts to pass the result of the function into getDataJSON
You need to leave out the parenthesis in this call
getDataJSON(doStuffWithData()); should be getDataJSON(doStuffWithData). The reason is that doStuffWithData is the function and doStuffWithData() is the retrun value from the function.
I have a handler (callback), an object to handle and four functions, which collect the data to object. In my case I wish to asynchronously call four data retrievers and when execution of all four is complete, handle the resulting object (something similar to the following):
var data = {};
function handle (jsObj) {}
// data retrieving
function getColorData () {}
function getSizeData () {}
function getWeightData () {}
function getExtraData () {}
data.color = getColorData();
data.size = getSizeData();
data.weight = getWeightData();
data.extra = getExtraData();
handle( data );
Of course, this code will not work properly. And if I chain data retrieving functions, they will be called one after another, right?
All four functions should be called asynchronously, cause they are being executed for too long to call them one by one.
Updated:
Thanks to everybody for your suggestions! I prefered $.Deferred(), but I found it slightly difficult to make it work the way I need. What I need is to asynchronously make a view, which requires four kinds of data (extraData, colorData, sizeData & weightData) and I have three objects: App, Utils & Tools.
Just a small description: view is created by calling App.getStuff passed App.handleStuff as a callback. Callback in the body of App.getStuff is called only $.when(App.getExtraData(), App.getColorData(), App.getSizeData(), App.getWeightData()). Before that Utils.asyncRequest passed Tools.parseResponse as a callback is called.
So, now the question is should I create four deferred objects inside each App.get*Data() and also return deferred.promise() from each of them?
And should I deferred.resolve() in the last function in my order (Tools.parseResponse for App.getExtraData in my example)?
var view,
App,
Utils = {},
Tools = {};
// Utils
Utils.asyncRequest = function (path, callback) {
var data,
parseResponse = callback;
// do something with 'data'
parseResponse( data );
};
// Tools
Tools.parseResponse = function (data) {
var output = {};
// do something to make 'output' from 'data'
/* So, should the deferred.resolve() be done here? */
deferred.resolve(output);
/// OR deferred.resolve();
/// OR return output;
};
// App
App = {
// Only one method really works in my example
getExtraData : function () {
var deferred = new jQuery.Deferred();
Utils.asyncRequest("/dir/data.txt", Tools.parseResponse);
return deferred.promise();
},
// Others do nothing
getColorData : function () { /* ... */ },
getSizeData : function () { /* ... */ },
getWeightData : function () { /* ... */ }
};
App.getStuff = function (callback) {
$.when(
App.getExtraData(),
App.getColorData(),
App.getSizeData(),
App.getWeightData()
)
.then(function (extraData, colorData, sizeData, weightData) {
var context,
handleStuff = callback;
// do something to make all kinds of data become a single object
handleStuff( context );
});
};
App.handleStuff = function (stuff) { /* ... */ };
/// RUN
view = App.getStuff( App.handleStuff );
I did not expect the code in my example above to work, it is for illustrative purposes.
I've been trying to solve this for quiet a long time and it still gives no result. The documentation for jQuery.Deferred() and discussions around this, unfortunately, did not help me. So, I would be very glad and greatful for any help or advise.
Conceptually, you would use a counter that gets incremented as each asynchronous call completes. The main caller should proceed after the counter has been incremented by all the asynchronous calls.
I think what you're looking for are Promises / Deferreds.
With promises you can write something like:
when(getColorData(), getSizeData(), getWeightData(), getExtraData()).then(
function (colorData, sizeData, weightData, extraData) {
handle(/*..*/);
}
)
The get*Data() functions will return a promise that they fulfill when their assynchronous call is complete.
Ex:
function getData() {
var promise = new Promise();
doAjax("getData", { "foo": "bar" }, function (result) {
promise.resolve(result);
});
return promise;
}
The when simply counts the number arguments, if all it's promises are resolved, it will call then with the results from the promises.
jQuery has an OK implementation: http://api.jquery.com/jQuery.when/
What I could suggest for this scenario would be something like that.
write a function like this
var completed = 0;
checkHandler = function() {
if(completed == 4) {
handle(data);
}
}
where completed is the number of positive callbacks you must receive.
As soon as every function receives a callback you can increment the "completed" counter and invoke the checkHandler function. and you're done!
in example
function getColorData() {
$.get('ajax/test.html', function(data) {
completed++;
checkHandler();
});
}
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 have the following functions that is called every 2 seconds to load some data. It registers the function [do] to do the stuff with the response. (the example is simplified).
function doRequest (){
$.ajax({ url: 'www.google.com.pe', success: function (response) {do(response)} });
}
function do (text){
var i = setInterval(doRequest, 2000);
}
I wonder if there is any way that I can create a function that is called every time the [do] function is called with out needing to add a call to the listener inside the do function. If there is any better way to do it with jQuery, like a plugin I'd appreciate the help.
[Edit] The idea is not whether it works or not. My question was about if I can add a custom listener to the "do" function which was already implemented. Something like addActionListener("do", "after", doSomeThingElse),sSo I could do some thing else just after the do function has finished.
First, your simplified version won't work, because you'd need to pass the do function instead of calling it.
function doRequest (){
$.ajax({ url: 'www.google.com.pe', success: _do });
}
But it sounds like you're asking how to run some other code every time do is invoked.
If do is only invoked inside the doRequest() function, then just add your other code to an anonymous function that invokes do at the right time.
function doRequest (){
$.ajax({ url: 'www.google.com.pe', success: function(response) {
// Run your other code
// or invoke another function.
_do(response);
} });
}
If you want it to be more generalized, you can create a function decorator that returns a function which invokes do after some other code.
function doFactory(fn) {
return function() {
fn.apply(this, arguments);
_do.apply(this, arguments);
}
}
then make functions like this:
var doFoo = doFactory(function() {
console.log("foo");
});
If your requirement is more specific of a pre-processing of response, you could rework it like this:
function doFactory(fn) {
return function(response) {
_do.call(this, fn.call(this, response));
}
}
Then have the fn manipulate and return response.
var doFoo = doFactory(function(response) {
return response + "foo";
});
If you want to keep existing code as it is, you could wrap do() in another function which in turn calls do() and your new function (say do_this_as_well()).
See the example below (I renamed do() to do_this() to avoid confusion around the reserved keyword do). This works because global functions are nothing but variables with function objects in them. These variables can be overwritten, in this case with a new function that calls the old one:
function do_this(response) { ... }
(function()
{
var previous=do_this;
do_this=function(response) { previous(response); do_this_as_well(); }
})();
Replace
success: do(response)
with
success: function(response) { do(response); do_this_as_well(); }
I am struggling to totally understand callbacks and i am stumbling at the final hurdle.
Within JS I am calling a function which then calls a PHP function using a dojo rpc Json Service. I have stepped through the function in firebug and the PHP is executing and returning me the correct response via the callback but I don’t know how to return the value to the initial JS variable that invoked the JS function? E.g.
JS Function 1
Function one(){
Var test = getPhp(number);
}
function getPhp(number)
{
this.serviceBroker = new dojo.rpc.JsonService(baseUrl + '/index/json-rpc/');
var result = serviceBroker.phpFunc(number);
result.addCallback(
function (response)
{
if (response.result == 'success')
{
return response.description;
//I am trying to pass this value back to the
//var test value in function one
}
}
);
}
Basically i now need to pass response.description back to my var test variable in function one.
Any help is appreciated
This is not possible, since the callback is run asynchronously. This means that the getPhp function returns before the callback is executed (this is the definition of a callback, and one of the reasons asynchronous programming is hard ;-) ).
What you want to do is create a new method that uses the test variable. You need to call this method when the callback is executed.
i.e.
function one(result) {
var test = result;
// Do anything you like
}
function getPhp(number, callback) {
this.serviceBroker = new dojo.rpc.JsonService(baseUrl + '/index/json-rpc/');
result.addCallback(
function (response)
{
if (response.result == 'success')
{
callback(response.description);
}
}
);
}
getPhp(number, function(result) { one(result); });
This last method creates an 'anonymous function' that is passed to the getPhp function. This function gets executed at the time the response arrives. This way you can pass data to the one(number) function after the data arrives.
The short answer is that you can't.
Do whatever you want to do with the data in the callback or functions you call from the callback. You can't return anything from it.
A much cleaner answer:
getPhp(number);
function one(data){
var test = data;
// do what you want
}
function getPhp(number)
{
this.serviceBroker = new dojo.rpc.JsonService(baseUrl + '/index/json-rpc/');
var result = serviceBroker.phpFunc(number);
result.addCallback(
function (response)
{
if (response.result == 'success')
{
one(response.description);
}
}
);
}