I have a javascript code which have to request the database (ajax). But I discovered that the inserts were wrong but with the right sql request. So I added an alert on which ajax request to know when the code is executed.
Here is the code :
$.post("/kohana-v3.3.5/ajax/update_simulation", {
id_simulation: id_simulation,
nom_simulation: nom_simulation,
sol_simulation: sol_simulation,
station_simulation: station_simulation,
iteration_simulation: iteration_simulation,
scenario_simulation: scenario_simulation
}
, function (result) {
console.log(result);
alert('update');
});
$.post("/kohana-v3.3.5/ajax/delete_pousses", {id_simulation: id_simulation}, function (result) {
console.log(result);
alert('delete');
});
$(this).prev('div').find('table .formRows').each(function (i) {
alert('here');
if (cpt % 2 == 1) {
//interculture
var $tds = $(this).find('td option:selected'),
culture = $tds.eq(0).val(),
date = $tds.eq(1).text();
itk = null;
} else {
//culture
var $tds = $(this).find('td option:selected'),
culture = $tds.eq(0).val(),
itk = $tds.eq(1).val();
date = null;
}
$.post("/kohana-v3.3.5/ajax/insert_pousses", {
id_simulation: id_simulation,
culture: culture,
date: date,
itk: itk,
rang: cpt
}, function (result) {
console.log(result);
alert('insert');
}); //Fin du post
cpt++;
}); //Fin du each
Each time I run that code, the order of the alert is always different ! Sometimes "insert update delete", sometimes "update, delete insert" ...
And it's a problem because if the delete is the last one, the insert will be removed. So, is it a normal way ? How should I resolve it ?
javascript can be executed asynchronously - and that's the reason why your ajax requests are not always executed in the same order. You can set them asnyc false (like here jQuery: Performing synchronous AJAX requests) or make something like promises (https://api.jquery.com/promise/) to wait for the ajax call to be finished.
greetings
AJAX requests are asynchronous, so you cannot guarantee an order if you trigger them as siblings like this.
In order to guarantee a fixed order, you need to make the subsequent call from the success block of its predecessor. Something like this:
$.post('/ajax/method1', { params: params },
function(result) {
$.post('/ajax/method2', { params: params },
function(result) {
$.post('/ajax/method3', { params: params },
function(result) {
});
});
});
You can use .promise to "observe when all actions of a certain type bound to the collection, queued or not, have finished."
https://api.jquery.com/promise/
Example Function
function testFunction() {
var deferred = $.Deferred();
$.ajax({
type: "POST",
url: "",
success: function (data) {
deferred.resolve(data);
}
});
return deferred.promise();
}
Calling Function
function CallingFunction()
{
var promise = testFunction();
promise.then(function (data) {
//do bits / call next funtion
}
}
Update
This may also help you out:
"Register a handler to be called when all Ajax requests have completed."
https://api.jquery.com/ajaxStop/
$(document).ajaxStop(function () {
});
Final note:
As of jQuery 1.8, the use of async: false is deprecated, use with $.Deferred.
you need to call post ajax method after by the success of previous one.
like:
$.post("/kohana-v3.3.5/ajax/update_simulation", {
id_simulation: id_simulation,
nom_simulation: nom_simulation,
sol_simulation: sol_simulation,
station_simulation: station_simulation,
iteration_simulation: iteration_simulation,
scenario_simulation: scenario_simulation
}
, function (result) {
console.log(result);
alert('update');
dleteajax();
});
function dleteajax()
{
$.post("/kohana-v3.3.5/ajax/delete_pousses", {id_simulation: id_simulation}, function (result) {
console.log(result);
alert('delete');
});
}
Related
Hi I want to execute a batch of ajax calls and get the response and then render the results for the user.
I'm using this code but it is not working because the render function executes before all the ajax responses have been collected.
serviceQuery: function (id) {
return $.getJSON(SERVICEURL + "/", id);
},
queryService: function(data){
var self = this;
var queries = [];
var results = [];
$.each(data, function (index, value) {
queries.push(self.serviceQuery(value.id));
});
$.when(queries).done(function (response) {
$.each(response, function (index,val) {
val.then(function (result){
results.push(result[0]);
});
});
self.renderResult(results);
});
},
renderResult: function(results){
$.each(results, function (index, value) {
///Error here cause the value.Name is undefined
console.info(value.name);
});
}
Any Idea on how to wait for all the ajax calls to finish before execute the render function?
Use .apply() at $.when() call to handle an array of Promises. Note also that .then() returns results asynchronously
let queries = [
// `$.ajax()` call and response
new $.Deferred(function(dfd) {
setTimeout(dfd.resolve, Math.floor(Math.random() * 1000)
// response, textStatus, jqxhr
, [{name:"a"}, "success", {}])
})
// `$.ajax()` call and response
, new $.Deferred(function(dfd) {
setTimeout(dfd.resolve, Math.floor(Math.random() * 1000)
// response, textStatus, jqxhr
, [{name:"b"}, "success", {}])
})
];
$.when.apply(null, queries)
.then(function() {
renderResult($.map(arguments, function(res) {return res[0]}));
});
function renderResult(results) {
$.each(results, function (index, value) {
console.info(value.name);
});
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js">
</script>
Change the $.each to a for loop. It is possible that the .then on the value isn't finished processing before the loop completes. $.each is synchronous but the .then generally, means it's a promise and isn't synchronous.
$.each(response, function (index,val) {
val.then(function (result){
results.push(result[0]);
});
});
Change too
for(var idx = 0; idx < response.length; idx++) {
results.push(response[idx]);
}
or keeping with the each you may just need to remove the .then call.
$.each(response, function (index,val) {
results.push(val[0]);
});
I see one potential issue here:
$.when(queries).done(function (response) {
$.each(response, function (index,val) {
val.then(function (result){
results.push(result[0]);
});
});
self.renderResult(results);
});
Basically, your pseudocode says this:
For each return value from your $.when() command, take the value of that and return a new promise (via val.then). However, because you never wait for the val deferred to run, results.push is not guaranteed to be called before your self.renderResult(results) call.
The code looks weird to me in that you would need two nested deferreds for an ajax call. So I think a larger meta issue is why do you need to do val.then in the first place. However, given the current code, you would need to do something like this:
var innerDeferreds = [];
$.each(response, function (index,val) {
innerDeferreds.push(val.then(function (result){
results.push(result[0]);
}));;
});
$.when(innerDeferreds).then(function() { self.renderResult(results); });
Again, my guess is that you don't need the val.then in the first place, but I would need to look in a debugger to see what the values of response, index and val are. (If you set up a jsfiddle that would be super helpful!)
I am facing the following synchronization issue. I wouldn't be surprised if it has a simple solution/workaround. The BuildMenu() function is called from another block of code and it calls the CreateMenuData() which makes a request to a service which return some data. The problem is that since it is an async call to the service when the data variable is being used it is undefined. I have provided the js log that also shows my point.
BuildMenu: function () {
console.log("before call");
var data=this.CreateMenuData();
console.log("after call");
//Doing more stuff with data that fail.
}
CreateMenuData: function () {
console.log("func starts");
data = [];
dojo.forEach(config.layerlist, function (collection, colindex) {
var layersRequest = esriRequest({
url: collection.url,
handleAs: "json",
});
layersRequest.then(
function (response) {
dojo.forEach(response.records, function (value, key) {
console.log(key);
data.push(key);
});
}, function (error) {
});
});
console.log("func ends");
return data;
}
Console log writes:
before call
func starts
func ends
after call
0
1
2
3
4
FYI: using anything "dojo." is deprecated. Make sure you are pulling all the modules you need in "require".
Ken has pointed you the right direction, go through the link and get familiarized with the asynchronous requests.
However, I'd like to point out that you are not handling only one async request, but potentionally there might be more of them of which you are trying to fill the "data" with. To make sure you handle the results only when all of the requests are finished, you should use "dojo/promise/all".
CreateMenuData: function (callback) {
console.log("func starts");
requests = [];
data = [];
var scope = this;
require(["dojo/_base/lang", "dojo/base/array", "dojo/promise/all"], function(lang, array, all){
array.forEach(config.layerlist, function (collection, colindex) {
var promise = esriRequest({
url: collection.url,
handleAs: "json",
});
requests.push(promise);
});
// Now use the dojo/promise/all object
all(requests).then(function(responses){
// Check for all the responses and add whatever you need to the data object.
...
// once it's all done, apply the callback. watch the scope!
if (typeof callback == "function")
callback.apply(scope, data);
});
});
}
so now you have that method ready, call it
BuildMenu: function () {
console.log("before call");
var dataCallback = function(data){
// do whatever you need to do with the data or call other function that handles them.
}
this.CreateMenuData(dataCallback);
}
I need to wait until all my ajax functions are done, and then continue the exectution.
My particular case is that I need to translate some fields in a form before submitting it. I translate them with an ajax call to an external site. Depending on some values in the form i would need to do more or less translations. When all the translations are done (if any) I have to validate the form with ajax, and if its valid, then submit.
This is my aproach:
First, I have a function that sends the ajax call and do stuff with the data received:
function translate(...) {
$("#ajaxCounter").val(parseInt($("#ajaxCounter").val()) + 1);
$.ajax({
...
success:function(data) {
...
$("#ajacCounter").val(parseInt($("#ajaxCounter").val()) - 1);
}
});
Then, when the form is to be submitted I execute the following code:
$("#form").submit(function() {
translatable_fields.each(function() {
translate(...);
});
while (parseInt($("#ajaxCounter").val()) > 0) { null; }
if (!(this).hasClass('ready')) {
$.ajax({
//validation
success: function(data) {
if (data['isValid']) {
$("#form").addClass('ready');
$("#form").submit();
}
}
});
}
return true;
});
The problem is that the while loop in the submit function never ends.
If I execute the code without the while loop I can see the ajaxCounter input increasing when the translation functions start and decreasing when they end.
You can achieve this in a much neater fashion using the deferred objects returned from a $.ajax call. First you should get the translate() function to return the deferred:
function translate(...){
return $.ajax({
// settings...
});
});
Then you can put all those promises in to a single array:
var requests = [];
translatable_fields.each(function(){
requests.push(translate(...));
});
Then you can apply that array to $.when:
$.when.apply($, requests).done(function(schemas) {
console.log("All requests complete");
// do something...
});
You can do this using deferred objects, but you do not need to use $.when.apply with an array if you are only interested in the final completion.
Instead you can chain parallel promises using the pattern promise = $.when(promise, another promise)
Change your translate to return the Ajax promise:
function translate(...) {
...
return $.ajax({
...
});
}
and your promise loop simply becomes:
var promise; // Start with an undefined promise - which is the same as a resolved promise for $.when
translatable_fields.each(function() {
promise = $.when(promise, translate(...));
});
// Wait for all promises to complete
promise.done(function(){
// now do the final code after all the ajax calls complete
});
Notes:
This does create an extra promise per call to $.when, but the overhead is very small and the resulting code is quite simple.
No, you can't just loop like this: the callbacks would never get a chance to be called.
I would do something like this:
function translateAllFields(done) {
var requestsInProgress = 0, doneCalled = false;
translatable_fields.each(function () {
++requestsInProgress;
$.ajax({
//...
success: function (data) {
//...
$("#ajacCounter").val(parseInt($("#ajaxCounter").val()) - 1);
}
}).always(function () {
if (--requestsInProgress === 0) {
done();
doneCalled = true;
}
});
});
if (requestsInProgress === 0 && !doneCalled) {
// in case translatable_fields was empty
done();
}
}
and then:
$("#form").submit(function (e) {
if (!(this).hasClass('ready')) {
e.preventDefault();
e.stopPropagation();
translateAllFields(function() {
$.ajax({
//validation
success: function (data) {
if (data['isValid']) {
$("#form").addClass('ready');
$("#form").submit();
}
}
});
});
}
});
You can use callback
function translate(..., callback) {
$.ajax({
...
success:function(data) {
...
callback(data);
}
});
};
And pass your after ajax code to it
$("#form").submit(function() {
translatable_fields.each(function() {
translate(..., function(result){
if (!(this).hasClass('ready')) {
$.ajax({
//validation
success: function(data) {
if (data['isValid']) {
$("#form").addClass('ready');
$("#form").submit();
}
}
});
}
return true;
});
});
});
Let's suppose I have some function called makeRequest(), which makes an AJAX request to a server.
Now let's suppose I am given the amount of times this request should be made, but I can't do them asynchronously but synchronously instead.
For instance, I am given the number 5, and I shall call makeRequest(), when it's done, I shall call it again, and when it's done, I shall call it again... I should end up calling it 5 times.
I'm no expert at JavaScript but I found it easy to handle asynchronous calls by the use of callbacks.
So, my makeRequest() function takes a callback argument that is to be executed when the request has succeeded.
In my previous example, I had to make the request 5 times, so the behaviour should look like:
makeRequest(function () {
makeRequest(function () {
makeRequest(function () {
makeRequest(function () {
makeRequest(function () {
});
});
});
});
});
How can I design this to behave the same for any argument given to me, be it 6, 12 or even 1?
PS: I have tried many approaches, the most common involving creating while loops that wait until a flag is set by a finished request. All of these approaches makes the browser think the script crashed and prompt the user to stop the script.
Simple, recursively call the ajax request, while keeping track of a count variable:
function makeRequest(count, finalCallback){
someAjaxCall(data, function(){
if(count > 1){
makeRequest(count - 1, finalCallback);
} else {
finalCallback && finalCallback();
}
});
}
finalCallback is a optional callback (function) that will be executed when all the requests are completed.
You can do it this way,
var i = 5; // number of calls to you function calling ajax
recurs(i); // call it initially
function recurs(count) {
makeRequest(function() {
count--; // decrement count
if (count > 1) {
recurs(count) // call function agian
}
});
}
Here I have written multiple Ajax calls using promises. This function will run synchronously. You can get the current position of response which is executed from Ajax.
var ajaxs = {
i : 0,
callback : null,
param : null,
exec_fun : function (i) {
let data_send = this.param[i];
let url = this.url;
this.promise = new Promise(function (res,rej) {
$.ajax({
url: url,
method: 'POST',
data: data_send,
dataType: 'json',
success: function(resvalidate){
res(resvalidate);
}
});
});
this.promise.then(function (resvalidate) {
let resp = resvalidate,
param = ajaxs.param,
pos = ajaxs.i,
callback_fun = ajaxs.callback_fun;
callback_fun(resp,ajaxs.i);
ajaxs.i++;
if( ajaxs.param[ajaxs.i] != undefined){
ajaxs.exec_fun(ajaxs.i);
}
});
},
each : function (url,data,inc_callback) {
this.callback_fun = inc_callback;
this.param = data;
this.url = url;
this.exec_fun(ajaxs.i);
}
};
let url = "http://localhost/dev/test_ajax.php";
let data_param = [{data : 3},{data : 1},{data : 2}];
ajaxs.each(url,data_param, function (resp,i) {
console.log(resp,i);
});
I currently have the following code:
function render(url1, url2, message) {
utility.messageBoxOpen(message);
$.getJSON(url1, function (items) {
// Do something
utility.messageBoxClose();
});
$.getJSON(url2, function (items) {
// Do something
});
}
When the function is executed a modal window appears to inform the user something is loading. Initially I only had one $getJSON request so when the request was done the modal window closed as per the code above.
I am looking to add another $getJSON request but want to close the modal window only when both $getJSON requests have completed.
What is the best way of achieving this?
You're looking for $.when()
All jQuery ajax requests (including shortcuts like getJSON) return deferred objects which can be used to control other actions.
var dfd1 = $.getJSON(url1, function (items) {
// Do something
});
var dfd1 = $.getJSON(url2, function (items) {
// Do something
});
$.when(dfd1, dfd2).then(function(){
//both succeeded
utility.messageBoxClose();
},function(){
//one or more of them failed
});
If you don't care whether the getJSONs come back successfully or not and instead only care that they are done you can instead:
$.when(dfd1, dfd2).done( utility.messageBoxClose );
A variable
function render(url1, url2, message) {
utility.messageBoxOpen(message);
var isOneDone = false;
$.getJSON(url1, function (items) {
// Do something
if(!isOneDone)
isOneDone = true;
else
utility.messageBoxClose();
});
$.getJSON(url2, function (items) {
// Do something
if(!isOneDone)
isOneDone = true;
else
utility.messageBoxClose();
});
}
You can replace the getJSON() call to one using $.ajax which accomplishes the same thing but gives you more flexibility:
$.ajax({
url: http://whatever,
dataType: 'json',
async: false,
data: {data},
success: function(data) {
// do the thing
}
});
Note the async:false part - this makes the code execution pause until the request is completed. So you could simply make your two calls this way, and close the dialog after the second call is completed.
function render(url1, url2, message) {
utility.messageBoxOpen(message);
$.when($.getJSON(url1, function (items) {
// Do something
utility.messageBoxClose();
}), $.getJSON(url2, function (items) {
// Do something
})).then(function () {
//Both complete
});
}
jQuery.when