Alternative for Multiple ajax to get data rails - javascript

I have 4 tabs on my page:
MY Queue | Today | Followup | Upcoming
on page load i fire 4 ajax calls to get data from controller for all these tabs and once i get data i create list for each of the tabs.
But as ajax is asynchronous i get anomalies in my data, are there any better ways to achieve this.
i have 4 ajax calls similar to below call:
$.ajax({
url: '/opd_clinical_workflow/get_appointment_lists',
dataType: 'json',
data: {
current_date: current_date,
department_id: current_department,
doctor: current_doctor,
status: current_status,
source: "list",
q: $( "#search_appointment").val(),
},
success: function(res){
console.log(tab,res)
_this.updateMyQueueSummary(res,id,tab);
},
error: function(err){
console.log(err);
}
});
updateMyQueueSummary:
Puts data in respective tabs
createSummaryAppointment:
Creates html for me and is called in updatesummary

Chaining AJAX is your best option:
$.ajax({
url: "somewhere/1"
})
.always(function(reponse1) {
//Do something with the response
document.write("<p>1</p>");
//Then call next part
$.ajax({
url: "somewhere/2"
})
.always(function(reponse2) {
//Do something with the response
document.write("<p>2</p>");
//Then call next part
$.ajax({
url: "somewhere/3"
})
.always(function(reponse3) {
//Do something with the response
document.write("<p>3</p>");
//Then call next part
$.ajax({
url: "somewhere/4"
})
.always(function(reponse4) {
//Do something with the response
document.write("<p>4</p>");
//Now finalize it
document.write("<p>Final</p>");
})
})
})
})
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
Notice i am using always instead of done because i don't have anywhere to actually get data from.

All the jQuery ajax methods return promise objects. This are kind of like an IOU note from a friend. You don't have the data now but it will be there at some point.
They are an extremely powerful tool if you want to avoid what is called "callback soup" where you nest ajax callbacks until the code is both unreadable and brittle.
var promise = $.ajax('/foo');
promise.pipe(function(data){
return $.ajax('/bar?foo=' + data['foo']);
});
promise.done(function(data){
// ajax call to '/bar?foo=' is complete
});
You can use jQuery.when to combine two promises. This is really useful when you want to fire two asynchronous ajax calls at once but you need the results of both to continue.
var calls = $.when( $.ajax('/foo'), $.ajax('/bar') );
calls.done(function(response_a, response_b){
// both ajax calls are complete.
});

Related

Cannot set $_SESSION variable when two synchronous ajax processing calls are sent

I have what might be a tricky question.
I am working on a form where it verifies a couple things on submit, using event.preventDefault(); to prevent the form from submitting if something went wrong. The issue here is that it sends multiple ajax requests at the same time, which seems to stop the php (which is processing the AJAX call) from modifying the $_SESSION variable.
I have determined this by changing the jquery ajax calls to process synchronously, allowing the $_SESSION variable to be changed.
My question is this: is there a way to allow the ajax calls to happen synchronously while allowing the $_SESSION variable to be modified during the process of those calls? I realize that the async:false for an AJAX call is deprecated, and obviously not the best solution.
Due to what each call does, it is not possible to combine the functionality of these two calls, although each call does not take long at all to process.
Example jquery code to explain how I am making these AJAX calls (some redaction and simplification, obviously):
$("#form-id").on('submit', function(event) {
$.ajax({
type: 'POST',
url: '/url/to/processing.php',
async:false, //fails without setting to false
...
});
});
...
$("#form-id").on('submit', function(event) {
$.ajax({
type: 'POST',
url: '/url/to/processing2ThatSetsSession.php',
async:false, //fails without setting to false
...
});
});
You have to concat the calls, to run one call after the other has ended.
I'll do it this way:
function ajaxPost(url, callback) {
$.ajax({
type: 'POST',
url: url
...
}).done(callback);
}
$("#form-id").on('submit', function(event) {
event.preventDefault(); // Always stop the event
// Do one ajax call and wait for the data
ajaxPost('/url/to/processing.php', function(data) {
// Do things with returned data and call the next ajax
ajaxPost('/url/to/processing.php', function(moredata) {
// Do something with moredata
// If everything is fine, re-post it but this time do not catch the event
$("#form-id").off("submit").submit();
});
});
});
You can add your own logic to show your error message in any callback and not continue with the next one.
With this I'll do an special method for multiple ajax form validation:
// This function will get an array of objects and
// do an ajax call and process the data, one after another
function multiAjax(calls, callback) {
var call = calls.shift();
if (call) {
var url = call.url;
post(url, function(data) {
var error = call.process(data);
if (error) {
callback(error);
} else {
multiAjax(calls, callback);
}
});
} else {
callback();
}
}
// This is the array of objects that multiAjax will process.
// You can add or remove elements to your likings, without modifying
// the submit event callback
var ajaxArray = [{
url: '/url/to/processing.php',
process: function(data) {
if (data.isWrong()) {
return "The data is wrong";
}
}
}, {
url: '/url/to/processing.php',
process: function(data) {
if (data != "OK") {
return "The data is not OK";
}
}
}];
// Now listen for the submit event
$("#form-id").on('submit', function(event) {
// Always stop the event
event.preventDefault();
// Do multiple ajax calls in one function call.
// Because the array is mutated inside multiAjax() (yeah, bad design but I've
// done this fast as an example), we slice() the array to get a new one
multiAjax(ajaxArray.slice(), function(error) {
if (error) {
// Show the error received
} else {
// submit the form the same way above
$("#form-id").off("submit").submit();
}
});
});
This is all untested code, but you get the point.
If one form submission is making two posts to the same PHP server, you should rethink the architecture instead of building complicated workarounds.
I would POST to a single PHP script that will do everything you need in the backend.
$.ajax({
type: 'POST',
url: '/url/to/all-processing.php',
... // send all the data needed by all processes
});
On the PHP side: all-processing.php
session_start();
require_once './process1.php';
require_once './process2.php';

ajax call won't execute synchronously

I have built a weather website that calls the flickr API 1st, then calls the yahoo API for the weather. The problem is that the data from the ajax call - from the yahoo API is not here in time for the page to load its content.
Some of the things I have used to try and slow the ajax call down:
setTimeout
wrapping the entire function that $.ajax(success: ) calls into another function, wrapping it in setTimeout
taking the callback function out of $.ajax(success: ), and putting into the $.ajax(complete: ) param
taking the data object that $.ajax(success: ) passes in, and copying that to another var, then going outside of ajax call and putting the function that handles the data inside of $.ajaxComplete(), passing new object var
There are more ways that I have tried to go about this, but I have been at it for 3 days and cannot find a solution. Can someone please help me here
Here is a link to the project
My Weather App On codeine.io
function RunCALL(url)
{
var comeBack = $.ajax({
url: url,
async: false,
dataType:"jsonp",
crossDomain: true,
method: 'POST',
statusCode: {
404: function() {console.log("-4-4-4-4 WE GOT 404!");},
200: function() {console.log("-2-2-2-2 WE GOT 200!");}},
success: function(data){
weatherAndFlickrReport(data);},
error: function(e) {console.log(e);}
});
}
Are you using jQuery? If so, you have to chain your callbacks. Which, at a high level, would looks something like:
//You might want to use .get or .getJSON, it's up to what response you're expecting...
$.getJSON('https://example.com/api/flickr', function(response) {
//This your callback. The URL would end up being https://example.com/api/yahoo/?criteria=lalalalala
$.getJSON('https://example.com/api/yahoo/', { criteria: response.propertyYouWant}, function(yahooResponse) {
//Do something with your response here.
});
});
Edit: I have updated your snippet with a working solution (based on the above AJAX requests) which now shows both your JSON objects ready for consuming. Looky here.

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);
});
}

How to wait for an async call from a callback using jQuery?

I'm using the select2 jQuery based replacement for combo boxes, and I have to define a callback to process the data I receive from a json rest web service.
The problem is that, in the same callback, I have to issue another GET request to get the total numbers of matching records, so that select2 can decide if it has to load more results (it has an infinite scroll feature)
The code is something like this:
$("#country").select2({
ajax: { // instead of writing the function to execute the request we use Select2's convenient helper
url: 'http://localhost:9000/api/countries',
dataType: 'json',
data: function(term, page) {
return {
filter: term,
page: page,
len: 10
};
},
results: function(data, page) {
return {
results: data, more: ????
};
}
}
});
The problem is I don't know how to issue an async request (I'm issuing a cross-domain request, and the docs says async is not supported in that case) and wait for it to finish before returning form the results callback.
The example from select2 page is like this:
results: function (data, page) {
var more = (page * 10) < data.total; // whether or not there are more results available
// notice we return the value of more so Select2 knows if more results can be loaded
return {results: data.movies, more: more};
}
The problem is that my web service returns the total number of records from a different endpoint, so I have to make another request, like this: http: //localhost:9000/api/countries?filter=term
any idea?
You can't wait for an async callback in javascript. You have to restructure your code to do all future work based on the async response from the actual callback.
If you need to make multiple consecutive ajax calls, then you issue the first one and in the success handler or response handler for the first ajax call, you issue the second ajax call and in the response handler for the second one, you carry out whatever you want to do with the data.
If see that you're using the .select2() framework. In the framework, the results callback is where the ajax call returns. It would be in that function that you would issue the second ajax call using normal jQuery ajax calls and in the success handler from that second ajax call, you would carry out whatever you're trying to do with the eventual data you got back. You won't be able to use the normal return value of the results callback because you won't have your final data yet at the point you need to return. I think this is just a limitation of .select2() in that it only supports a single ajax call. It just means you can't use a little bit of the built-in behavior and have to apply the result yourself with your own code, but it doesn't mean you have to throw out .select2() for everything else you were using it for.
It looks like you might want to just hook the change event directly and not use their built-in ajax stuff since it doesn't look like it really provides you with much if you need two serialized ajax calls.
I studied the source code on select2, and finnally came out with this solution
var ajax = {
url: 'http://localhost:9000/api/countries',
len: 3,
};
$("#country").select2({
query: function(options) {
var data = {
filter: options.term,
page: options.page,
len: ajax.len
};
$.ajax({
url: ajax.url,
data: data,
dataType: 'json',
type: 'GET',
success: function(data) {
$.ajax({
url: ajax.url + '/count',
data: { filter: options.term },
dataype: 'json',
success: function(resp) {
var total = parseInt(resp, 10);
var more = (options.page * ajax.len) < total;
options.callback({results: data, more: more});
}
});
}
});
},
});
As you can see, when te first fetch (ajax.url) completes I issue another request (ajax.url + '/count') and only when this second request completes I call options.callback, efectively serializing both ajax calls...
In fact the ajax function from select2 has more functionality, such as throttling and dropping out-of-order responses, I just ported them too, but I left them out of this response in order not to complicate the example...
In addition to jfriend00's answer (which is excellent, BTW) I found the followgin workaround, which is basically to issue the request synchronously, which in spite jquery docs it seemd to work (at least with chromium 18.0 and jquery 1.8.0)
I'm just posting it in case anybody find it useful...
var config = {
url: 'http://localhost:9000/api/countries',
len: 20,
term: ''
}
$("#country").select2({
ajax: {
url: config.url,
dataType: 'json',
data: function(term, page) {
config.term = term;
return {
filter: term,
page: page,
len: config.len
};
},
results: function(data, page) { // parse the results into the format expected by Select2.
var more = false;
$.ajax({
url: config.url + '/count',
data: { filter: config.term },
cache: false,
async: false,
success: function(resp) {
var total = parseInt(resp, 10);
more = (page * config.len) < total;
},
async:false
});
return {
results: data, more: more
};
}
}
});

Categories

Resources