$.ajax Deferred object - javascript

These two codes (1)(2) seems to work in the same manner to me.
My questions are:
1) Are these two codes equivalent?
2) If yes why? If not what should I prefer and why?
(1)
$.ajax({
url: backendRouter.generate('feedback_send'),
type: 'POST',
dataType: 'json',
data: data
success: callback,
done: function () {
// some code
}
});
(2)
$.ajax({
url: backendRouter.generate('feedback_send'),
type: 'POST',
dataType: 'json',
data: data
success: callback
}).done(function () {
// some code
});

Yes, the two codes are equivalent, except that (by mistake?) you've left success: callback in the latter.
However IMHO the latter is preferred as deferred objects are far more flexible than supplying a callback directly to $.ajax.
In particular, using deferred objects allows for much better separation of logic and responsibility between initiating the AJAX call and the processing of the results of that call. Also, some of the AJAX helper functions don't support an error callback. If I write:
function doAjax() {
return $.get(...);
}
I can then attach arbitrary numbers of done and fail handlers to the result of that function call, without ever having to pass those handlers into the doAjax function.
I can also combine the returned promise() object with other promises using $.when(), $.pipe(), etc, for very powerful synchronisation between multiple asynchronous events (including other AJAX calls, timers, animations, etc). I can't do that using success:

Related

$.ajax success function wait

Due to the (my perceived) nature of the $.ajax GET I'm not sure what I'm looking for is possible in this method, but thought I should ask as it's causing me some trouble. I am in no way an expert on JS, apologies if this is obvious, the title is poor, couldn't think of a different way to say it.
What I'm doing - using $.ajax to get values from the back-end API functions (these work). In the success property I'm using an anonymous function to pass more data across that would not otherwise be brought back. When stepping through the console breakpoints in the Chrome console the success function is often skipped as it's not brought back quick enough.
What I would like - in the example below, I don't want getStuff to complete, before its success DoSomething has returned, as I'm missing loads of data from the API which I need to pass through to DoSomething - is this possible to achieve?
Example
function getStuff(id, hid) {
$.ajax({
type: "GET",
url: "/api/GetMyStuff/" + id,
contentType: "application/json; charset=utf-8",
dataType: "json",
success: function (vData) {
DoSomething(vData, id, hid); // not always being hit
}
});
}
You could use the async option and set it to false to get a synchronous call:
function getStuff(id, hid) {
$.ajax({
type: "GET",
url: "/api/GetMyStuff/" + id,
contentType: "application/json; charset=utf-8",
dataType: "json",
async: false, // HERE
success: function (vData) {
DoSomething(vData, id, hid); // not always being hit
}
});
}
But that is not a good way to do ajax request, you should rethink your code organization (like giving a callback function to your getStuff method and calling it in the success callback).
What you are trying to do is impossible. Asynchronicity is "leaky", meaning that any asynchronous action requiring a callback (e.g., $.ajax()) necessarily forces asynchronous control flow all the way up the call chain.
Please note that in the near future (thanks to async/await in ES7), this situation gets a little better. The asynchronicity is still leaky, but the new keywords make the refactoring scenario a little easier than hand-jamming callbacks everywhere.

AJAX callbacks when getting multiple XML files

I'm trying to get a better understanding of how callbacks work.
In this example, I want to get two or more XML files using AJAX, then extract the content I need from them, and then store that data in an array outside of the AJAX call. I want to use the "dataExt" array to plot a google chart, but I am getting hung up implementing callbacks properly. I guess my brain just isn't big enough yet!
Here's my code snippet.
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js"></script>
<script>
// List of xml files.
var xmlFeed = ['rss-feed1.xml', "rss-feed2.xml"];
// An array to store each "dataString" from each XML file.
var dataExt = [];
for(var i = 0; i < xmlFeed.length; i++) {
$.ajax({
type: "GET",
dataType: "xml",
async: true,
url: xmlFeed[i],
contentType: "text/xml; charset=UTF-8",
success: function(xml){
var content = $(xml).find("content");
var dataString = (content.text());
console.log(dataString);
// Need to push "dataString" to "dataExt" array.
// dataExt = dataExt.push(dataString); <-- this doesn't work
}
}) // close ajax
} // close loop
console.log(dataExt[0]);
console.log(dataExt[1]);
</script>
When you make an AJAX request, the call to $.ajax() returns immediately. It does not wait around for the content to come back over the network. In your example, the console.log() statements are being called before the callbacks have been completed.
What you want is a single callback that gets executed once all the data you need from your various requests has been fetched. To do this you need some sort of synchronization between the various AJAX calls that you're doing. jQuery alone doesn't support this that well.
You can roll this yourself. However, this is a common enough design problem that whole libraries have been written to handle it.
Do some Googling on the Promise pattern. Once you've done that, have a look at the Q library, which is one of several implementations of the pattern. They have done most of the hard work of synchronizing multiple AJAX requests for you.
Example:
function xmlPromise(name) {
return Q.promise(function (resolve, reject, notify) {
$.ajax({
type: "GET",
dataType: "xml",
async: true,
url: name,
contentType: "text/xml; charset=UTF-8"
})
.done(function (data) {
resolve(data);
}).fail(function () {
reject();
});
});
};
var promises = [ xmlPromise('1.xml'), xmlPromise('2.xml') ];
var results = [];
Q.allSettled(promises).then(function(responses) {
console.log(responses[0].value);
console.log(responses[1].value);
results.push(responses[0].value);
results.push(responses[1].value);
});
In this example, the xmlPromise() function creates a promise object for you based on the URL you want to fetch. This promise represents a unit of work that will be completed at some time in the future. When the AJAX call you create in the promise returns successfully, it calls the resolve() method which lets Q know that the promise has been fulfilled, and the data is ready for use.
Once the promises are constructed, we pass an array of them into Q.allSettled(), which actually fires off the requests. Because these requests are asynchronous, they are executed in parallel. Once all of the promises either resolved or been rejected, Q will call the function you pass into the then() method, and pass in the results of your AJAX calls.
in original example console logs are fired before request finishes.
hope this example is what you mean:
function getXml(file, successCallback){
$.ajax({
type: "GET",
dataType: "xml",
async: true,
url: file,
contentType: "text/xml; charset=UTF-8",
success: successCallback
}) // close ajax
}
function sc(xml){
var content = $(xml).find("content");
var dataString = (content.text());
console.log(dataString);
// Need to push "dataString" to "dataExt" array.
// dataExt = dataExt.push(dataString); <-- this doesn't work
// do whatever else you want next
}
pages ['1.xml', '2.xml'].forEach(function(v, i){
getXml(v, sc);
})

Synchronous javascript call to MVC 4 Controller action

I'm writing piece of code, and I need some functions to be executed sequential, I used ajax call but it is asynchronous and i faced problems
function GetLibraryActivities(libraryName, callback) {
$.ajax({
dataType: "json",
url: "/WorkflowDesigner/GetLibraryActivities/?libraryName=" + libraryName
}).done(function (data) {
console.log(data);
return data;
});
}
then I tried to work with callback function, but it didn't work too.
function GetLibraryActivities(libraryName, callback) {
$.ajax({
'url': "/WorkflowDesigner/GetLibraryActivities/?libraryName=" + libraryName,
'type': 'GET',
'success': callback
});
}
GetLibraryActivities("Petrophysics", function (data) {
petrophysicsData = data
});
when I try to use petrophysicsData variable under the code it returns unidentified , I need a mean to call a function in synchronous way, any help will be appreciated thanks.
Your main problem here is that you are trying to 'return' something from an AJAX callback. You cannot expect an Async AJAX function to return values to the script function that called it, because the function that called it moved on after the AJAX call started. That is the point of an Async call, it allows the javascript to move on and not have to wait for the server communication to complete.
The best way to handle this is to call all portions of a function in sequential order by using callbacks, never planning to use a return.
So, rather than returning data it would be best to instead call a function that processes data from within the callback function. It can be a little inconvenient, but when working with Async calls, it is best to assume that you can only go deeper into your call stack, rather than returning anything back out of it.
So, rather than your first option...you would want to do something like this instead...
function GetLibraryActivities(libraryName, callback) {
$.ajax({
dataType: "json",
url: "/WorkflowDesigner/GetLibraryActivities/?libraryName=" + libraryName
}).done(function (data) {
console.log(data);
ProcessResults(data);
});
}
Or, simply perform your processing within the callback.
Pre jQuery 1.8, there was the 'async' option, that would allow you to force Javascript to wait for the ajax call to process...but this locks up the browser while it is processing, and has since been deprecated.
If you simply return the Ajax promise like this:
function GetLibraryActivities(libraryName, callback) {
return $.ajax({
'url': "/WorkflowDesigner/GetLibraryActivities/?libraryName=" + libraryName,
'type': 'GET'
});
}
you can do this to use the value:
GetLibraryActivities("Petrophysics").done(function (data) {
// do something with petrophysics data
});
and chain them "sequentially" with then like this:
GetLibraryActivities("Petrophysics").then(GetLibraryActivities("Astrophysics")).done(function(pertro, astro){
// Do something with both results};
});
In this example both will load in parallel, but the done will only be called with both promises have completed (in any order).
If you have loads of these to load, you can use $.when to process multiple promises.

Carry out Javascript/jQuery functions in a sequence

I want to make some wine. And my function does:
function wine(){
growGrapes();
process(grapes);
makeWine();
bottle();
}
However, Since my functions often consist of $.ajax() request, some other functions get carried out first. I have used the success tool, but it helps for one ajax request only.
success:function(result){
//Some Code
}
What I actually want is a sequence.
Literally, grapes get processed before growing them. What is a easiest approach?
jQuery Deferred Objects & Promises are the way to go. http://api.jquery.com/category/deferred-object/
They supports running multiple tasks in parallel or series using $.when(PassArrayOfPromisesToRunInParallel) to run processes in parallel and promise.then() to run items sequentially.
Call the next function in the success handler of the $.ajax call of the previous function!
Example:
function growGrapes(){
// lines of code
$.ajax({
success: function(result){
// call next function here - process(grapes); and so on...
}
});
}
The above makes sure the functions get called sequentially after the other..
You can make your Ajax calls synchronous (in sequence) by ensuring you have async: false in your $.ajax() settings.
For example:
$.ajax({ url: 'url',
async: false,
dataType: 'json',
success: function(data) {
}
});
First solution :
Make your ajax call syncronous by setting async : false when setting up your ajax call
$.ajax
({
async : false,
/* other settings */
});
Warning: This solution causes the UI to hand on intensive processing. This should never be used when doing anything rigorous on the server. My recommendation for using this is to only use it in checking flags or loading simple data.
Second solution :
As stated in the comments, use jQuery promises to set up the ordering. Here is a tutorial
I'll try to come back and provide a code example for this solution soon
Third solution :
Make your next call the success handler, or call the next step from the success handler
$.ajax
({
success : NextStep,
/* other settings */
})
One solution is to use queue() function. This way you can execute as many functions as you want
var ajaxQueue = $({});
$.ajaxQueue = function(ajaxOpts) {
// queue the method. a second call wont execute until this dequeues
ajaxQueue.queue(function(next) {
// for this example I serialize params, but you can save them in several variables
// and concat into ajaxOpts.data
var params = method_that_get_params_and_serialize_them();
ajaxOpts.data = params;
ajaxOpts.complete = function() {
next();
};
$.ajax(ajaxOpts);
});
};
then your functions should be like this:
function growGrapes(){
$.ajaxQueue({
cache: false,
type: "POST",
url: "someUrl",
dataType: "json",
data: "", // we fill data inside ajaxQueue() method
success: function( response) {
//do things with response
}
});
}
If you want to keep it tidy and clean to let people see how your calls are made, you can simply pass a callback function to another like this:
function growGrapes(callback) {
$.ajax({
...
success: function (){
// Something
if (typeof callback === typeof Function) callback();
},
...
});
}
function wine(){
growGrapes(function (){
process(grapes);
});
}

Accessing JSON string Outside of Jquery Ajax Call

I am wondering is these is any way to access the results of a jquery ajax call in the form a traditional var set to function fashion. For example consider:
function getPoints(){
//An array of JSON objects
var Points;
$.ajax({
url: "js/retrievePointsDataJson.php",
dataType:'json',
type: 'POST',
}).done(function(data){
//console.log(data);
Points.append(data);
});
console.log(Points);
return Points;
}
The commented out console.log show the array of json objects whereas the outer one does not. Now, i have tries this:
var Points = $.ajax({ ...});
And i see the response text within a larger object, but am unsure how to access the responseText. console.log(Points.responseText) yields an undefined variable.
Is this possible with this approach? Here is another question that received a check mark with a similar issue.
I have another solutions, which is the encapsulate my code within the done() function and i will have access to all my data. I was just curious if what i am attempting to do is even doable.
Thank you.
yes it is possible, however, you must wait for the request to be complete before doing so. However, since you can't effectively force the return to wait until the data exists, you're only options are to return a deferred object instead, or re-write the function in such a way that allows it to accept a callback.
function getPoints(){
return $.ajax({
url: "js/retrievePointsDataJson.php",
dataType:'json',
type: 'POST'
});
}
getPoints().done(function(data){
console.log(data);
});
or
function getPoints(callback){
return $.ajax({
url: "js/retrievePointsDataJson.php",
dataType:'json',
type: 'POST',
success: callback
});
}
getPoints(function(data){
console.log(data);
});
Because the Ajax call is done asynchronously you shouldn't return it from the outside function. This would require that you somehow block until the asynchronous call completes. Instead you could pass in a callback function to the getPoints function that will handle the logic of using the points.
function getPoints(callback){
$.ajax({
url: "js/retrievePointsDataJson.php",
dataType:'json',
type: 'POST',
}).done(function(data){
callback(data);
});
}
The asynchronous nature of ajax can make things harder if your used to imperative programming, but it will make your user interface much more responsive.
The log you're calling in the outer function is working with an undefined variable because the function is asynchronous. You can't return it from getPoints because it won't have finished. Any work with the Points variable needs to happen inside the callback (the function passed to done).

Categories

Resources