I have a function which I need to run after several Ajax calls are complete. Each AJAX call is currently called one after another and currently do work within their respective callbacks. Is this a candidate for a promise chain as each ajax call only runs if its respective test is true? And I don't necessarily need these calls to be serially run just that I get one callback at the end (when all the Ajax calls which fulfilled the criteria to be run completed their respective callbacks).
if(MyTest1){
RunMyAjaxCall1(param1,param2,successAjaxCallback1,failureAjaxCallback1);
}
if(MyTest2){
RunMyAjaxCall2(param1,param2,successAjaxCallback2,failureAjaxCallback2);
}
if(MyTest3){
RunMyAjaxCall3(param1,param2,successAjaxCallback3,failureAjaxCallback3);
}
if(MyTest4){
RunMyAjaxCall4(param1,param2,successAjaxCallback4,failureAjaxCallback4);
}
Sample AJAX Call
RunMyAjaxCall1 = function (param1,param2, successCallback, failureCallback) {
$.ajax({
type: "POST",
datatype: 'json',
url: "/WebServices/GlobalWebService.asmx/webservicemethod1",
data: JSON.stringify({ param1: param1, param2: param2 }),
contentType: 'application/json; charset=utf-8',
success: function (data) { successCallback(data.d); },
error: function (data) { failureCallback(data.d) }
});
}
You could use this:
var requests = [];
if (MyTest1) {
requests.push(RunMyAjaxCall1(param1,param2,successAjaxCallback1,failureAjaxCallback1));
}
// ...continue with other if statements
$.when.apply(null, requests).always(function () {
// All handlers used are done (either succeeded or failed)
});
Since you said you were making AJAX calls, I assume you're using $.ajax. The value returned from calling $.ajax is a Promise. The RunMyAjaxCallX functions need to return this in order for this scheme to work.
Using requests allows for a dynamic way to group requests. The if statements determine which are executed and included.
The use of $.when (with passing requests as an argument) creates a new Promise, encompassing all requests that you determined should be included. Then using the .always() method, it will run when all included requests are completed.
Here's an example of it working: http://jsfiddle.net/vmw8H/
Some reference:
http://api.jquery.com/jQuery.when/
http://api.jquery.com/deferred.always/
Related
I have a function that, right now, retrieves a JSON file via AJAX call and prints out its contents on the console. However, I can't get it to read the file. I know that the location is formatted correctly, but the json variable will always return a value of null, indicating a nonexistent file.
function loadSettings(){
//loads setting list from mcc.json
var options = (function() {
var json = null;
$.ajax({
'async': false,
'global': false,
'url': "js/mcc.json",
'dataType': "json",
'success': function (data) {
json = data;
}
});
return json;
})();
console.log(options);
}
Did I do something wrong with my AJAX call?
The success/complete callback occurs only when the ajax call is completed. So nothing is actually being returned by your function ("return json").
In AJAX, you need to get the data object, then call what should run next, because any success or complete callback functions will happen AFTER the code you're executing, when the response from the server comes back.
Basically, you are returning the json variable, before the ajax call completes.
AJAX is an async call by it's name itself, so it is a non blocking code, so your
return json will be immediately (which you assigned to null), after some time when the response came back from server the success callback will be executed, which still has a access to the variable (json) declared in the outer closure.
instead try returning a promise, and resolve that promise when data came in success callback. and in the use end of the promise, use then with a callback to get the data when its resolved with the data.
EDIT
Here in edit, I am editing your code and adding to this answer, to use a promise and immediately return the promise in order to work it with the same pattern
function loadSettings(){
//loads setting list from mcc.json
var options = (function() {
var defer= $.Deferred(); // create a deferred-promise
$.ajax({
'async': false,
'global': false,
'url': "js/mcc.json",
'dataType': "json",
'success': function (data) {
defer.resolve(data);
}
});
return defer.promise(); // return the deferred promise instead
})();
options.then(function(data){
console.log(data);
});
}
Since you're assigning to options the returned value from an immediately invoked function, you should bare in mind that javascript won't stop its execution or wait for any async request to be resolved thus your IIFE will return right away (in your case, with json set to null)
if you need to process the returned data, do it within the success callback
The AJAX call has not completed when you return your JSON Data. Someone mentioned using a promise, but the alternative is shown below. This calls doTheNextThing function after the AJAX completes. Handle your settings in that new function.
function loadSettings(){
var options = (function() {
$.ajax({
'async': false,
'global': false,
'url': "js/mcc.json",
'dataType': "json",
'success': function (data) {
doTheNextThing(data);
}
});
})();
}
//------------------------------------------
function doTheNextThing(data)
{
//Now process your load settings here
}
Im building a browser based application.
On window.load, i call a jquery AJAX GET function which is supposed to get me some gamedata from the server.
Once the data is there, i parse this data into my javascript and build some stuff up.
For that reason, i set async to false.
Now looking at this function, i want to call another function on ajax.success, but i want to use the freshly returned data as well as the data that was sent to this very function as a parameter (from another async ajax call).
How can i do this ?
thanks
window.onload = function(){
ajax.getFleets();
ajax.getShips(fleets);
manager.draw() // can only draw if both ajax functions have received data !)
}
getShips: function(fleets){
$.ajax({
type: "GET",
url: "getGameData.php",
datatype: "json",
data: {
type: "ships"
},
error: ajax.error,
success: ajax.mergeData, //want to pass fleets and returnData to this function
async: false
});
},
Also, on a general note, using async: false is the correct approach for this, right ?
thanks,
$.ajax will pass your callback function the result of the AJAX request, so you can use a simple anonymous function to get both parts in. Let's say you have your callback function defined like this:
ajax = {};
ajax.mergeData = function(fleets, ajaxResult) {
console.log(fleets);
console.log(ajaxResult);
};
You can define your callback function as:
function(result) { ajax.mergeData(fleets, result); }
Putting that all together, your getShips function will look like this:
ajax.getShips = function(fleets) {
$.ajax({
type: "GET",
url: "getGameData.php",
datatype: "json",
data: {
type: "ships"
},
error: ajax.error,
success: function(result) { ajax.mergeData(fleets, result); }
});
};
To answer your second question: async: false is a deprecated option, so you can't rely on it to work. It will also lock up the browser until the server responds, which makes for a really bad user experience. It's far better to use callback functions or promises to carry out an action when your AJAX requests complete. Here's a short summary of the promises concept that might be useful.
Which leaves the last, big question: how do we chain these calls together so getShips() depends on the result of getFleets(), and manager.draw() depends on both? With just two calls, you could get away with just using callbacks; getShips' success callback invokes getFleets, and getFleets' callback invokes manager.draw. This is the easiest way to get an A → B → C dependency chain working – but we could also use jQuery's promises, which might make our code easier to follow.
First, let's change getFleets to return the result of calling $.ajax:
ajax = {};
ajax.getFleets = function() {
return $.ajax({ /* Details elided here */ });
}
Next, let's update getShips to make an AJAX request for that data, and return a promise that returns the combined data:
ajax.getShips = function(fleets) {
return $.ajax({ /* Details elided again */ })
.done(function(ajaxResult) { return mergeData(fleets, ajaxResult); });
};
This is a bit trickier; $.ajax returns a promise that will eventually resolve to our ship data. When it does, we'll call the function in .done that returns the merged data. And that .done also returns a promise – so getShips is now returning a promise that will eventually return the merged data.
So now we can write our window.onload function to chain all these together, and only call manager.draw when all the data's available:
window.onload = function() {
ajax.getFleets()
.done(ajax.getShips)
.done(manager.draw);
}
I'm using jQuery ajax to request data which will then be made into different kinds of charts or tables.
I've put the queries I want to run into an object and send the requests. runQuery() returns a jQuery promise. The data returned when the promise is done is correct. [Edit]Since the ajax requests run asynchronously they may not come back in the order they were issued [/EDIT] and I have no way of know which query the returned data was for.
function refreshData(){
for(var key in baseQueries){
runQuery(baseQueries[key])
.done(function(data){
console.log("Query data for "+key);
// want to call different charting functions
// based upon which results are returned
});
}
};
runQuery(obj) { // "public" function
var params = $.extend({},defaults,obj);
return sendRequest(queryUrl,params)
}
sendRequest(url,data,method){ // "private" function
method = method || "GET";
return $.ajax({
type:method,
url:url,
dataType:"json",
data:data
})
.fail(function(error){
console.log(error);
});
}
In this case the console logs the value of key during the last iteration over the baseQueries object. For example if there are three items in my baseQueries object and the the last item in my baseQueries object is
"This-is-the-last-query":"queryparam1,queryparam2,etc"
Then when all three of the ajax calls resolve I get three logs of "This-is-the-last-query". Which is not helpful because it doesn't tell me which query the data belongs to.
This is similar to the idea of the infamous javascript loop issue but I really don't see how the answer of attaching the value to a DOM element could be used here.
How do I match up which query call goes with which promise? How to I pass the key through the ajax call and return it with the promise data.
Edit
Don't think this is a duplicate of the indicated thread. I see how they are related, but not how to use that to solve this. Suggested duplicate doesn't mention jquery, ajax, promises, or asynchronous issues. It is also marked as a duplicate for another thread that doesn't mention any of those things either.
The solution shown either involves using the dom element to hold the information (which doesn't apply here) needed for the onclick or by adding a closure, but I don't see how to do that when there is already data being returned.
If you pass jQuery ajax a parameter that it knows nothing about, it ignores it and you can access it later.
Here we set a parameter for your extra value (mykey) then we have access to it later for the instance we are using:
function refreshData() {
for (var key in baseQueries) {
runQuery(baseQueries[key], key)
.done(function(data) {
console.log("Query data for " + this.myKey);
// "this" here is the ajax for which the promise (ajax) gets resolved,
// and we return that promise so it works here.
// want to call different charting functions
// based upon which results are returned
});
}
};
runQuery(obj,key) { // "public" function
var params = $.extend({}, defaults, obj);
return sendRequest(queryUrl, params,,key)
}
sendRequest(url, data, method, key) { // "private" function
method = method || "GET";
return $.ajax({
type: method,
url: url,
dataType: "json",
data: data,
myKey:key
})
.fail(function(error) {
console.log(error);
});
}
if you want to check if jquery promise is resolved you can check with jqueryPromise.state() which returns either pending, resolved or rejected depending on state.
If you are sending multiple ajax requests and you want to know when they are completed you can put them into array ([$.ajax(...),$.ajax(...)]) and pass it to
$.when like
var requests = [$.ajax(...),$.ajax(...)];
$.when.apply($, requests).done(function() {
console.log('all requests completed');
});
if you want to build something complex with promises I would suggest using bluebird or less complex rsvp
I am curious if I am using the deferred incorrectly and if not - is there a more eloquent way.
So, I am firing this ajax request multiple times during a persons page view. Now, I understand I can only resolve the deffered once, so I reset the "def" appropriately. But I am wondering if I can assign the ajax call to the variable, and pass in the unique identifier and resolve the deferred?
I need things to happen either on the success or error of the ajax request.
So, I have an ajax request.
// some code. I set some var.
var def = $.Deferred();
// more code
$.ajax({
dataType: "json",
url: url,
data: params,
success: function(d) {
// resolve my deferred and pass a unique identifier
def.resolved('onSucc');
},
error: function(d) {
def.resolved('onErr');
};
});
To something like?
var def = $.ajax({
dataType: "json",
url: url,
data: params,
success: function(d) {
// resolve my deferred and pass a unique identifier
return def.resolved('onSucc');
},
error: function(d) {
return def.resolved('onErr');
};
});
Also, is there a need to return a promise here? My code seems to work ok without it - but I am curious if I am missing out.
You are actually not using deferred correctly. Deferred objects are for creating promise chains from scratch when making a non-promisified API promisified. The way you're using it is called the deferred anti pattern.
Promises chain, so instead of reverting to callbacks and deferred objects, you can simply use the chaining function then that transforms a promise into a new promise:
return $.ajax({
dataType: "json",
url: url,
data: params,
}).then(function(result){
console.log("got result!", result);
return someTransformOn(result); // use return values, can also return promise
}); // error case gets handled for you
Which would let you do:
functionReturningAjax().then(function(transformedValue){
// access to transformed value here
},function(err){
// then accepts an optional second argument for errors, use it to handle errors.
});
I find a fantastic bug when I use jQuery ajax to do a request to web service:
var AjaxResult;
login = function () {
AjaxResult = "";
$.ajax({
type: "POST",
url: KnutBase.getServiceUrl() + "ServiceInterface/HmsPlannerWebService.asmx/AuthenticateLogin",
data: { username: this.username, password: this.password },
dataType: 'jsonp',
success: function (response) {
//the response value is 'success'
AjaxResult = response;
},
error: function (data, status, e) {
alert("error:" + e);
}
});
//alert(AjaxResult);
//if i do not alert a message here, this function will return a null value
//however, i do alert here, then i get a 'success' value.
return AjaxResult;
}
How could this happen.
I am confused as to why the AjaxResult returns its value before the ajax method set response value to it.
Ajax is asynchronous call to your method and it do the processing in the back for server trip. You will not get the result until the success method is called. You can read more about jquery ajax here
You can set async attribute to false of ajax() to make a synchronous call.
EDIT: Using deferreds --
$.Deferred can be used to run code after some other code has completed. Ajax is an excellent example, and all of the jQuery ajax methods (except .load) implement the $.Deferred interface. You can use $.when to watch deferreds. For example:
login = function () {
return $.ajax({...
The deferred gets returned from the method above, so you can do the following:
$.when(login()).done(function (response) { ... });
Any code that needs to be synchronized with the ajax response has to go in the .done callback.
Note that this is not much different than just using the success callback with $.ajax. The point is that all of your work that relies on what is returned from Ajax needs to be done in these callbacks.
Since ajax is asynchronous, return AjaxResult is actually executed before the ajax result completes. You can not return in this way; you need to perform some action in the callback of the ajax requests or use deferred.
It has to do with the fact that AJAX is asynchronous (that's actually the meaning of the first A in it). alert stops the code until you press the button, and the request has time to complete. With no alert, the function proceeds immediately, the AJAX has barely begun its work, and of course the value is still empty.
Generally, you can't have functions return values that they get from AJAX. Set up a callback for it, and deal with it when it comes.
How can Ajax do asynchronous request and return a synchronous result
You can't. It's just impossible. If your code really tries to do that, it will fail.