jQuery $.post() in recursive way (loop) - javascript

I learn jQuery and don't understand this situation:
When running this code in debug mode all work well. But when running this code normal, calback function don't starts. Why?
In non debug mode I have -> "Start" -> "End 10"
Browser: Google Chrome.
var nrPost = 10;
$("#buttnX").click(function() {
alert("Start");
GoPosts();
End();
});
function End() {
alert('End ' + nrPost);
};
function GoPosts() {
$.ajaxSetup({async:false});
var url = "http://......";
var data = { ... };
$.post(url, data, Callback, 'json');
};
function Callback(response) {
if (response.error) {
return;
}
nrPost--;
if (nrPost > 0) [
GoPosts();
} else {
return;
}
};

You had an extra }; in your code. I changed it around a bit to use jQuery and put it up on jsfiddle.
http://jsfiddle.net/rH8RV/19/
It should alert: "Start" and then "End 10", that's correct based on how you wrote your code. Were you expecting anything else?

I don't know what you're planning to do with your recursive implementation, but if that's all, you could actually do this:
function startLoop(nrPost) {
// No need to put this in a loop
$.ajaxSetup({ async: false });
for (var i = 0; i < nrPost; i++) {
alert('Start ' + i);
var url = 'http://......';
var data = {};
$.post(url, data, function (response) {
if (response.error)
return;
alert('End ' + i);
}, 'json');
}
}
$('#buttnX').click(function () { startLoop(10) });
Hope that helps!

I imagine you are expecting the display to be:
"Start"
"End 0"
This is unlikely to work with your solution.
Your Ajax call $.post(url, data, Callback, 'json'); is asynchronous. This means that once the $.post method returns, the request is sent to the URL you have provided. However, Callback is not called until JQuery receives the answers. What happens immediately is that GoPosts terminates and the program continues. It comes back to line 5 of your code, inside the anonymous function in your click handler. At that point, End() is called and alerts "End 10".
You probably want to put your call to End in Callback instead:
function Callback(response)
{
if (response.error)
{
return;
}
nrPost--;
if(nrPost>0)
GoPosts();
else
{
End(); // You only want to end once you have made you nrPost calls to GoPost
return;
}
};

Related

JQuery/Ajax and when/done/promise confusion

I am trying to accomplish the following:
1) Get data some source and "do something with it".
2) Get data some other source and "do something with it".
3) The datafetching should preferrably run asynchronous (at the same time ie. the second one should not wait for the first one to complete).
4) When both are completed, some business logic runs - but ONLY when they are completed.
I have created a small JSFiddle to show how I thought this could work - but unfortunately it does not:
a) The datafetching calls are executed sequentially.
b) The business logic from step 4 above executes before the datafetching has even begun...
Fiddle here: https://jsfiddle.net/LeifFrederiksen/emttmhm7/
$.when(
getOneThing(),
getAnotherThing()
).done(
function() {
console.log("Got it all");
$("#Output").append("<BR>Got it all");
}
);
function getOneThing() {
commonFunctionToGetStuff("oneKindOfThings",renderOneKindOfThings);
}
function getAnotherThing() {
commonFunctionToGetStuff("anotherKindOfThings",renderAnotherKindOfThings);
}
function commonFunctionToGetStuff (listTitle,successFunction) {
var url = "https://httpbin.org/get";
$.ajax({
url: url,
type: "GET",
headers: { "accept": "application/json;odata=verbose" }
}).success(function (data) {
console.log("Calling renderfunction for " + listTitle);
$("#Output").append("<BR>Calling renderfunction for " + listTitle);
successFunction(data);
console.log("Back from renderfunction for " + listTitle);
$("#Output").append("<BR>Back from renderfunction for " + listTitle);
});
}
function renderOneKindOfThings(data) {
// Do something with the data...
console.log("Doing oneKindOfThings.");
$("#Output").append("<BR>Doing oneKindOfThings.");
}
function renderAnotherKindOfThings(data) {
// Do something with the data...
console.log("Doing anotherKindOfThings.");
$("#Output").append("<BR>Doing anotherKindOfThings.");
}
Any help clearing up how the structure should be is highly appreciated.
I need to maintain the structure where the function that performs the actual Ajax call is kind of generic, and can be called by simple wrapper functions with parameters controlling what datasource to use - like it is in the example :-)
Regards
Leif
You need to return the promise from your commonFunctionToGetStuff-method and the methods that call it. Otherwise, you're passing in undefined into your when-function which will immediately execute the done-callback. Also you have a few erroneous callback names (it's done or then, not success).
function getOneThing() {
return commonFunctionToGetStuff("oneKindOfThings",renderOneKindOfThings);
}
function getAnotherThing() {
return commonFunctionToGetStuff("anotherKindOfThings",renderAnotherKindOfThings);
}
function commonFunctionToGetStuff (listTitle,successFunction) {
var url = "https://httpbin.org/get";
return $.ajax({...})
.then(function (data) { ...});
}

How to ensure that a function containing multiple $.ajax calls runs fully synchronously and also allows for browser repaints as it executes

I've been working on getting a function written to:
1) Process an input array using $.ajax calls to fill an output array (below this is inputList)
2) Below is what I have, but I'm having issues with it:
requestData(), when I call it, runs straight through to processing the outputList array without having fully populated/filled it - it puts one value into it then starts to process that, but the function still apparently runs on seperately to the subsequent processing asynchronously. I need it to be fully synchronous so that it does not return until the inputList array has been fully processed.
I'm not seeing the browser repainting the div that has its html updated on every call of the runajax() function - I'm attempting to do this with a setTimeout.
3) I've set the ajax request to be synchronous (async : false) - but this doesn't seem to help
I've tried to use jQuery's $.when to provide an ability to ensure that everything gets called in sequence - but clearly I'm not doing this correctly.
Would appreciate any help - I've asked previous related questions around this and had some useful help - but I've still not resolved this!
Thanks
//declare holding function requestData - expects a non-empty input data array named inputList
function requestData() {
//declare inner function runajax
function runajax() {
if(inputList.length > 0) {
//get first item from inputlist and shorten inputList
var data = $.trim(inputList.shift());
function getData() {
//send the data to server
return $.ajax({
url: 'sada_ajax_fetch_data.php',
cache: false,
async: false,
method: "post",
timeout: 2000,
data: {
requesttype: "getmydata",
email: encodeURIComponent(data)
}
});
}
function handleReturnedData (response) {
response = $.trim(decodeURIComponent(response));
//update the div inner html
if(response == "Failed") {
$('#fetchupdatestatus').html('There was an error retrieving the data you requested!');
} else {
$('#fetchupdatestatus').html('The item returned was '+response);
}
//add the response from ajax to the end of the outputList array
outputList.push(response);
//set up the next ajax call
var doNextBitOfWork = function () {
runajax();
};
//call setTimeout so that browser shows refreshed div html
setTimeout(doNextBitOfWork, 0);
//return
return $.when();
}
//do the next ajax request and response processing
return getData().done(handleReturnedData);
} else {
//did the last one so return
return $.when();
}
}
//kick off the ajax calls
runajax();
}
var inputList = new Array();
var outputList = new Array();
.....load +/- 100 values to be processed using ajax into array inputList
requestData();
.....process stuff in array outputList
.....etc
There was my answer with "you're doing it wrong" earlier, but then I just decided to show, how you can do it (almost) right: https://jsfiddle.net/h4ffz1by/
var request_maker = {
working: false,
queue: [],
output: [],
requestData: function(inputList) {
if (request_maker.working == true) {
return false;
}
request_maker.output = [];
request_maker.working = true;
while (inputList.length > 0) {
var data = $.trim(inputList.shift());
request_maker.queue.push(data);
}
console.log(request_maker.queue);
request_maker.doTheJob();
return true;
},
doTheJob: function() {
current_data_to_send = request_maker.queue.shift();
console.log(current_data_to_send);
if (typeof current_data_to_send != 'undefined' && request_maker.queue.length >= 0) {
$.ajax({
url: '/echo/json/',
cache: false,
method: "post",
timeout: 2000,
data: {
requesttype: "getmydata",
email: encodeURIComponent(current_data_to_send)
},
success: function(data, status, xhrobject) {
console.log(xhrobject);
request_maker.handleReturnedData(data);
},
});
} else {
request_maker.working = false;
console.log('all data has been sent');
}
},
handleReturnedData: function(response) {
console.log(response);
response = $.trim(decodeURIComponent(response));
//response= 'Failed';//uncomment to emulate this kind of response
if (response == "Failed") {
$('#fetchupdatestatus').append('There was an error retrieving the data you requested!<br/>');
} else {
$('#fetchupdatestatus').append('The item returned was ' + response + '<br/>');
request_maker.output.push(response);
}
request_maker.doTheJob();
if (request_maker.working == false) {
console.log('all requests have been completed');
console.log(request_maker.output);
}
}
}
inputList = [1, 2, 3, 4, 5, 6];
if (request_maker.requestData(inputList)) {
console.log('started working');
}
if (!request_maker.requestData(inputList)) {
console.log('work in progress, try again later');
}
Note that I've changed request path to jsfiddle's ajax simulation link and replaced html() with append() calls to print text in div. The calls are made and get handled in the same order as it is in inputList, still they don't lock user's browser. request_maker.output's elements order is also the same as in inputList.
Have in mind, that you will need to add error handling too (probably just a function that pushes 'error' string into output instead of result), otherwise any ajax error (403/404/502, etc.) will get it "stuck" in working state. Or you can use complete instead of success and check request status right there.
UPD: Answer to the question: you cannot get both. You either use callbacks and let browser repaint inbetween asynchroneous requests or you make requests synchroneous and block browser untill your code finished working.
UPD2: There actually is some information on forcing redraw, however I don't know if it will work for you: Force DOM redraw/refresh on Chrome/Mac

Access to the post request's argument data in succeed/complete/error functions

I have a bunch of jQuery AJAX post requests wrapped up in a for loop. Each request provides a different parameter. My question is, in the event of the request failing, how can you tell what parameter data was passed by that request?
When I try:
for(i = 0; i < toSubmit.length; i++) {
$.post('doSomething.php',
{id: toSubmit[i]},
function(data) { /* Do something here */ },
'json')
.error(function() {
console.log(toSubmit[i] + " didn't work!");
});
}
...the error function will just output the last value in toSubmit, because the i pointer has progressed all the way through the for loop, while the requests are fired asynchronously. The same thing occurs in the success and complete function; the way I've got around that is to make sure the returned JSON contains the respective id; but if the request fails, I can't use this workaround.
Is there a way for me to get at this information, or is there a better way of firing off these requests?
It seems you have to set the correct context for the method call. You can look into the jQuery proxy() method which allows you to provide the correct context for the callback.
Try something like this:
for(i = 0; i < toSubmit.length; i++) {
var ctx = {id: i};
$.post('doSomething.php',
{id: toSubmit[i]},
function(data) { /* Do something here */ },
'json')
.error( $.proxy(function() {
console.log(this.id + " didn't work!");
}, ctx) );
}​
try this:
beforeSend:function(jqXHR, settings){
jqXHR.parameters = { /* data or parameters store here*/}
}
error: function(jqXHR, textStatus, errorThrown){
var params = jqXHR.parameters
}

Why does this function not return JSON string?

function addphoto()
{
var ajaxRequest = initAjax();
if (ajaxRequest == false)
{
return false;
}
// Return Ajax result when the state changes later
ajaxRequest.onreadystatechange = function()
{
if(ajaxRequest.readyState == 4)
{
alert(ajaxRequest.responseText);
return ajaxRequest.responseText;
}
}
// Capture form elements
var values = {
"category" : encodeURIComponent(document.addphoto.category.options[document.addphoto.category.selectedIndex].value),
"photo_title" : encodeURIComponent(document.addphoto.photo_title.value),
"photo_descrip" : encodeURIComponent(document.addphoto.photo_descrip.value)
}
var queryString = '?', i = 0;
for (var key in values)
{
if (i != 0)
{
queryString += '&'
}
queryString += key + '=' + values[key];
i++;
}
// Execute Ajax
ajaxRequest.open("POST", "ajaxcheckform.php" + queryString, true);
ajaxRequest.setRequestHeader("Content-type", "application/x-www-form-urlencoded");
ajaxRequest.setRequestHeader("Content-length", queryString.length);
ajaxRequest.setRequestHeader("Connection", "close");
ajaxRequest.send(null);
}
function ajaxCheckform(formname)
{
var response = addphoto(); // <--This is undefined and not sure why
var responseObj = JSON.parse(response);
if (responseObj.success == 1)
{
// Successful form!
alert(responseObj.success_text);
}
else
{
// Error!
alert(responseObj.error);
}
}
I'm sure I must be making some basic error somewhere, but I'm having trouble locating it. In this script, ajaxCheckform() is a function that executes one of several similar functions. Above, I included addphoto(), which is one of several functions I'll need that look like this.
On a side note, I'd love to know I can call upon a function dynamically. The addphoto() function will be only one such function being called up at that moment and I'm trying to find a way to pass formname as the function I need. I've searched Stackoverflow and Google. I've found nothing that works.
Note, I'm aware of jQuery, but I'm not there yet. I need this function to work first.
It is not addphoto() thats undefined but response is undefined. ajaxRequest is asynchronous and the addphoto() function will return before the request completes.
try this
function addphoto() {...
// Return Ajax result when the state changes later
ajaxRequest.onreadystatechange = function()
{
if(ajaxRequest.readyState == 4)
{
alert(ajaxRequest.responseText);
var responseObj = JSON.parse(ajaxRequest.responseText);
if (responseObj.success == 1) {
// Successful form!
alert(responseObj.success_text);
}
else {
// Error!
alert(responseObj.error);
}
}
}
....
}
function ajaxCheckform(formname) {
addphoto();
}
That's because response is set to the return of addphoto(), which is nothing. What you want to do is have ajaxCheckForm get called when the AJAX call is completed:
// Return Ajax result when the state changes later
ajaxRequest.onreadystatechange = function()
{
if(ajaxRequest.readyState == 4)
{
ajaxCheckform(ajaxRequest.responseText);
}
}
Then your ajaxCheckform will work with that data:
function ajaxCheckform(responseText)
{
var responseObj = JSON.parse(responseText);
if (responseObj.success == 1)
{
// Successful form!
alert(responseObj.success_text);
}
else
{
// Error!
alert(responseObj.error);
}
}
You can't return from an event handler (which onreadystatechange is).
You have to do the work inside that event handler.
addphoto() does not return anything (or rather, returns inconsistently) ... the onreadystatechange event's handler is returning the value, but there is no caller that will receive that json string.
I'd highly suggest that you abstract these details away with something like jquery ... just follow the docs for suggested usage and this code will be much simpler
You're sending a GET style parameter list to a POST method.
You need to send that string in the body of your HTTP request.
var response = addphoto(); // <--This is undefined and not sure why
The addphoto() function never has a return statement in it, so it returns undefined. And the ajaxRequest is asynchrous and wont return immediately.

Change function on javascript prototype

I want to change the XMLHttpRequest send function so that a function is called before the request is made and after the request is complete. Here is what I have so far:
var oldSend = XMLHttpRequest.prototype.send;
XMLHttpRequest.prototype.send = function() {
//this never gets called
oldOnReady = this.onreadystatechange;
this.onreadystatechange = function() {
oldOnReady();
ajaxStopped();
}
ajaxStarted();
// according to http://www.w3.org/TR/XMLHttpRequest/
// there's only ever 0 or 1 parameters passed into this method
if(arguments && arguments.length > 0) {
oldSend(arguments[0]); //gets to here, calls this method, then nothing happens
} else {
oldSend();
}
}
function ajaxStarted() {
ajaxCount++;
document.getElementById("buttonClicky").innerHTML = "Count: " + ajaxCount;
}
function ajaxStopped() {
$("#isRunning")[0].innerHTML = "stopped";
ajaxCount--;
document.getElementById("buttonClicky").innerHTML = "Count: " + ajaxCount;
}
However, I'm doing something wrong here because once it hits the oldSend() call, it never returns or triggers the onreadystatechange event. So I must be doing somethingclickCount wrong here. Any ideas? I set a breakpoint and it gets hit just fine when I call this:
$.ajax({
type: "GET",
url: "file.txt",
success: function(result) {
//this never gets called
document.getElementById("myDiv").innerHTML = result;
}
});
So my new function is getting called. I guess just don't know how to call the old function. Any ideas on how to fix this code so that the Ajax Request is actually made and my new callback gets called?
Note: I'm aware of the JQuery events that essentially do this. But I'm doing this so I can get it to work with any Ajax call (Mootools, GWT, etc). I am just happening to test it with Jquery.
You need to call old functions in the context of this.
E.g.: oldSend.call(this)

Categories