Javascript value changes outside jquery.post - javascript

I have a weired bug that is driving me crazy for some time now. I have the following piece of code:
function isLoggedIn() {
var loggedIn = false;
var requestData = {
action: 'explore-webdesign-is-loggedin',
request_url: location.protocol + '//' + location.host + location.pathname,
async: false,
data: null
};
jQuery.post(ajaxurl, requestData, function(data) {
var dataObj = JSON.parse(data);
if (dataObj.success === true) {
loggedIn = true;
alert("1.) " + loggedIn);
}
});
alert("2.) " + loggedIn);
return loggedIn;
};
Javascript spitts out both alerts, which is fine, however the first one says '1.) true' while the second one gives me '2.) false'. What is going on?

AJAX requests are asynchronous - your final alert is being executed while the AJAX is in progress - the first alert finishes once the AJAX is done (hence why it's in the callback).
You don't return from an async method like you are trying to do, instead you would pass a callback function in to be executed once the AJAX is done:
function isLoggedIn(callback) {
...
...
jQuery.post(ajaxurl, requestData, function(data) {
...
...
if (dataObj.success === true) {
loggedIn = true;
callback(loggedIn);
}
});
}
Then call like so:
isLoggedIn(function(data) {
if (data) //user is logged in
});

Related

How do I stop my code from executing until an asynchronous piece of code has completed?

Today I've been struggling to get some JavaScript code working.
The goal of this code is to have all of the files names returned by the ajax function calls populated within the fileNameObj before the recordFormSubmission function is called.
async function uploadSubmissionFiles(files)
{
//Get an array for the names of each key of the files object
var filesKeys = Object.keys(files);
var fileSubmissionErrors = [];
var formElementPK = "";
//Create an object that keeps track of the file names that are saved to the server
var fileNamesObj = {};
//For each question where we uploaded files, call an ajax function
//where we transfer the files from the client to the server
var promiseToUploadFiles = () => {
return new Promise((resolve,reject) =>
{
for(let i = 0; i < filesKeys.length; i++)
{
formElementPK = filesKeys[i].match("(?<=fpk).*");
console.log(formElementPK);
if(files[filesKeys[i]]["type"] == "canvas")
{
var canvasElement = files[filesKeys[i]]["response"];
var canvasImageBase64 = canvasElement.toDataURL().split(';base64,')[1];
//Use ajax to upload the file to the server
$.ajax(
{
url: "./includes/ajaxColdfusion/fillout/submittedFileUpload.cfc?method=uploadSubmittedCanvasImage",
type: "POST",
data: canvasImageBase64,
enctype: 'multipart/form-data',
contentType: false,
processData: false
}
)
.then( function(response)
{
var responseElements = response.getElementsByTagName("string");
var resultMessage = responseElements[0].innerHTML;
console.log("File upload result for " + canvasElement.id + ": " + resultMessage);
if(resultMessage == "success")
{
//On success, responseElements[1] contains the name of the file that was saved to the server. We record it!
fileNamesObj[formElementPK] = responseElements[1].innerHTML;
}
else
{
//On failure, responseElements[1] contains the error message for what happened during the upload
fileSubmissionErrors.push(responseElements[1].innerHTML);
fileNamesObj[formElementPK] = "error.png";
}
console.log(fileNamesObj);
}
);
}
else if(files[filesKeys[i]]["type"] == "fileUpload")
{
var fileUploadForm = files[filesKeys[i]]["response"];
var formData = new FormData(fileUploadForm);
var fileFieldName = document.getElementById("question" + formElementPK + "FileUpload").getAttribute("name");
formData.append("fileFieldName", fileFieldName);
//Use ajax to upload the file to the server
$.ajax(
{
url: "./includes/ajaxColdfusion/fillout/submittedFileUpload.cfc?method=uploadSubmittedFile",
type: "POST",
data: formData,
enctype: 'multipart/form-data',
contentType: false,
processData: false
}
)
.then( function(response)
{
var responseElements = response.getElementsByTagName("string");
var resultMessage = responseElements[0].innerHTML;
console.log("File upload result for " + fileUploadForm.id + ": " + resultMessage);
if(resultMessage == "success")
{
//On success, responseElements[1] contains the name of the file that was saved to the server. We record it!
fileNamesObj[formElementPK] = responseElements[1].innerHTML;
}
else
{
//On failure, responseElements[1] contains the error message for what happened during the upload
fileSubmissionErrors.push(responseElements[1].innerHTML);
fileNamesObj[formElementPK] = "error.png";
}
console.log(fileNamesObj);
}
);
}
}
var retObj = {
"fileSubmissionErrors" : fileSubmissionErrors,
"fileNames" : fileNamesObj
}
console.log("Resolved the promise!");
resolve(retObj);
});
}
return await promiseToUploadFiles();
}
async function recordFormSubmission(data, formPK, fileNames)
{
var JSONData = JSON.stringify(data);
var JSONFileNames = JSON.stringify(fileNames);
var submissionErrors = [];
console.log("Filenames at trigger of record form submission: ", fileNames);
console.log("Now waiting 2 seconds with await new Promise(r => setTimeout(r, 2000));");
await new Promise(r => setTimeout(r, 2000));
console.log("Fileanmes after 2 seconds of triggering record form submission", fileNames);
$.ajax(
{
url: "./includes/ajaxColdfusion/fillout/recordFormSubmission.cfc?method=recordFormSubmission",
type: "POST",
data: "JSONData=" + JSONData + "&formPK=" + formPK + "&JSONFileNames=" + JSONFileNames,
enctype: 'multipart/form-data',
cache : false
}
).
done(function(response)
{
//If successful...
console.log("Record form submission successful:" + response.getElementsByTagName("string")[0].innerHTML);
}
).
fail(function(response)
{
//If not successful...
console.log("Record form submission failed");
submissionErrors = [response.getElementsByTagName("string")[1]];
}
);
return submissionErrors;
}
uploadSubmissionFiles(filesToUpload.then( function(fromUploadSubmissionFiles)
{
var fileSubmissionErrors =
fromUploadSubmissionFiles["fileSubmissionErrors"]
var fileNames = fromUploadSubmissionFiles["fileNames"];
//Query the database to save our form data to the database
var formDataSubmissionErrors = recordFormSubmission(dataToRecord, formPK, fileNames);
}
Above are all the important parts of this code. As it stands right now though, recordFormSubmission() gets called and executes before the fileNamesObj variable is populated with file names that are returned from the ajax function. This must mean that the code is not waiting on the ajax to finish executing before continuing with execution, even though a promise has been implemented.
I suppose my question here is, what am I doing wrong if I want the JavaScript code to wait until all of the ajax functions are completed before calling recordFormSubmission()?
I could probably think of a polling solution here, but I'd like to think that there's a way to solve this with promises or better practice.
This is the result of running the modified code to return an awaited promise. It also shows how the ajax functions return and the filenames are obtained in the window of time between waiting two seconds and when the critical point where the filenames must exist occurs.
The solution to this was found within an answer to another question: https://stackoverflow.com/a/66747114/16511184
Credit to #Heretic-Monkey for providing it.
The solution: put an await in front of each ajax() function call. Do NOT encapsulate anything within a promise.
I'm new to the concept of Promises in Javascript, so I unknowingly made an error of encapsulating promises inside of eachother. It's very important to note that the jquery ajax() function returns a promise. In my problem, I was triggering asynchronous code that I was not using await for. Instead I used await for a function that contained an ajax() function. The await wouldn't wait very long at all because all the code was doing was triggering the ajax() function and leaving, not waiting for it to finish (the very crux of what I wanted to occur).
Here's a good analogy:
You're going to the supermarket with your friend. You tell your friend to split up from you and grab a carton of eggs. You wait for them, and once they come back from grabbing the eggs you'll both go to the checkout counter together.
Effectively, the problem I had was I decided to wait until I was done telling my friend to get eggs to go to the checkout counter. I needed to wait for my friend to SPECIFICALLY get the eggs.
try this.
async function uploadSubmissionFiles(files)
{
...
var promiseToUploadFiles = () => {
return new Promise((resolve,reject) =>
{
$.ajax(
{
url: "./includes/ajaxColdfusion/fillout/submittedFileUpload.cfc?method=uploadSubmittedFile",
type: "POST",
data: formData,
enctype: 'multipart/form-data',
contentType: false,
processData: false
}
)
.then( function(response)
{
var responseElements = response.getElementsByTagName("string");
var resultMessage = responseElements[0].innerHTML;
console.log("File upload result for " + fileUploadForm.id + ": " + resultMessage);
if(resultMessage == "success")
{
//On success, responseElements[1] contains the name of the file that was saved to the server. We record it!
fileNamesObj[formElementPK] = responseElements[1].innerHTML;
}
else
{
//On failure, responseElements[1] contains the error message for what happened during the upload
fileSubmissionErrors.push(responseElements[1].innerHTML);
fileNamesObj[formElementPK] = "error.png";
}
resolve(fileNamesObj);
}
)
.catch(function(e) {
reject(e)
});
}
}
return await promiseToUploadFiles();
...
}
await uploadSubmissionFiles(filesToUpload).then( function(fileNameObj)
{
recordFormSubmission(..., fileNameObj)
}).catch(function(e) {
console.error(e);
});
It is necessary to wrap your promise in a function and wait for its completion after the call. When you have assigned a promise to a simple variable, the request immediately starts working and the code runs on without waiting for completion

AJAX response text for success

Right now I'm modifying my AJAX request to be asynchronous but I wanted to know if there was something similar to var reponse = $.ajax({ in success. Before I had my code as:
var response = $.ajax({
type : "GET",
url : url,
data : parameters,
cache : false,
async : false
}).responseText;
return response;
I tried doing using the first data argument but that just returns the parameters. Is there something similar I can use in success?
success : function(response) {
callBack(response);
}
Because the request is asynchronous you cannot just return the response.
jQuery uses something called "promises", which you can return instead:
function getUser(id) {
return $.ajax({
url: "/user",
data: { id:id },
});
}
So, whenever you want to get a user you just call the function:
var userRequest = getUser(123);
The userRequest variable now contains a "future promise". In other words, sometime in the future it will be ready for you to use it.
You cannot use it straight away but you can create a function that will run when it finally is ready. That is done using the .done() method:
userRequest.done(function (user) {
console.log("The user " + user.name + " has been loaded!");
});
If you, for example, also want to load the user's profile alongside the user then you can create two requests and then use the $.when() method:
var userRequest = getUser(123).
profileRequest = getProfileForUser(123);
$.when(userRequest, profileRequest).done(function (user, profile) {
console.log(user.name + " is " + profile.age + " years old");
});
Read more about promises over at jQuery.

Get data back from ajax call within ajax call

I'm need some help figuring out how to get back data from the second ajax call, not the first.
I have this method that calls my ajax calls
var projectWithIssues = getProjects().done(function(result) {
....
}
When I look at the results from this, I get back the results on my first ajax call(getEnt_PodType().done()). I want to get the results from the second ajax call within getProjects(). I understand the reason I'm getting the first results back is because I have the return on the first ajax call. However, If I don't have a return there. I get a undefined on the line above. How can I return the data from the second call?
function getEnt_PodType() {
var ent_PodType;
var oDataUrl = //URL to my data;
return $.ajax({
url: oDataUrl,
type: "GET",
async: true,
beforeSend: function (xhr) {
xhr.setRequestHeader("ACCEPT", accept);
},
success: function (xhr, textStatus) {
}
});
}
function getProjects() {
return getEnt_PodType().done(function (res) {
var ent_PodType;
if (res.d.results != undefined) {
ent_PodType = res.d.results[0].Ent_PodType;
}
console.log("The ent pod type value is " + ent_PodType);
var QUERY_FILTER =
"$filter=Ent_PodType eq '" + ent_PodType + "'";
var url = restUrl + QUERY_FILTER;
// I want to return the results from this ajax call
$.ajax({
url: url,
type: "GET",
async: true,
beforeSend: function (xhr) {
xhr.setRequestHeader("ACCEPT", accept);
},
success: function (xhr, textStatus) {
//projects = parseODataResultTest(xhr);
//return projects;
}
});
});
}
Thanks in advance!
Try utilizing pattern found at deferred.then
// first request
var request = $.ajax(url1),
chained = request.then(function( data ) {
console.log(data) // first request response data
// return second request
return $.ajax(url2)
});
chained.then(function( data ) {
console.log(data) // second request response data
// data retrieved from url2 as provided by the first request
});
var request = $.ajax("https://gist.githubusercontent.com/guest271314/23e61e522a14d45a35e1/raw/62775b7420f8df6b3d83244270d26495e40a1e9d/ticker.json"), // first request , `html` document
chained = request.then(function( data ) {
console.log(data) // `["abc"]`
// return `data` from second request
return $.ajax("https://gist.githubusercontent.com/guest271314/6a76aa9d2921350c9d53/raw/49fbc054731540fa68b565e398d3574fde7366e9/abc.txt")
});
chained.then(function( data ) {
console.log(data) // `abc123`
// data retrieved from url2 as provided by the first request
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
Use .then instead of .done, it allows better chaining of functions.
Break your code apart so that the two AJAX calls are in separate functions, and have both those functions return the result of their $.ajax call. You can then use:
func1().then(func2).then(...);
func2 will be passed the result of the first AJAX call, and then the result of that will be passed to whatever function is in the final then.
In your case you can also put the call to parseODataResultTest in the chain and then the final function will (eventually) be called with the required data, i.e.:
getEnt_PodType().then(getProjects).then(parseODataResultTest).then(function(projects) {
// use projects here, and _only_ here because it won't
// be in scope or defined anywhere else
...
});

javascript and order of execution of functions

My Class looks like
function classUser() {
var userName;
var firstName;
var lastName;
var sessionid;
}
classUser.prototype.set_user_name = function (user_name) {
this.userName = user_name;
}
classUser.prototype.set_first_name = function (first_name) {
this.firstName = first_name;
}
classUser.prototype.set_last_name = function (last_name) {
this.lastName = last_name;
}
classUser.prototype.get_curr_session = function () {
return this.sessionid;
}
classUser.prototype.save = function () {
$.ajax({
type: "POST",
url: "http://myapihost.com:8080/api/1.0/user/",
data: JSON.stringify(this),
dataType: "json",
success: function (apiResponse) {
var currSessionID = apiResponse.sessionId;
this.sessionid= currSessionID;
},
error: function (apiResponse) {
alert("error : " + apiResponse);
this.sessionid= "Error";
}
});
}
I call them as
var User = new classUser();
User.set_first_name(userFirstName);
User.set_last_name(response.last_name);
User.set_user_name(response.username);
User.save();
var currSessionID = User.get_curr_session();
Sometimes, get_curr_session is called before success: call.
Question :
I tried returning sessionid from success itself so that save() function does the job. That is not working. hence i split across 2 functions.
Can I do it in one call itself? if I have use 2 functions - how do i make sure that it works all the time.
I could actually put assigning the currSessionID within success, however that breaks class sanctity. I have seen other solution like using "done", not sure if that would help here.
=======================================
I modified the code as below
classUser.prototype.save = function (callback) {
$.ajax({
type: "POST",
url: "http://myapihost.com:8080/api/1.0/user/",
data: JSON.stringify(this),
dataType: "json",
success: function (apiResponse) {
var currSessionID = apiResponse.sessionId;
this.sessionid= currSessionID;
callback(null, currSessionID);
},
error: function (apiResponse) {
alert("error : " + apiResponse);
this.sessionid= "Error";
callback("error", null);
}
});
}
When I call
User.save(mycallback);
function mycallback(error, sessId){
if(error) {
console.log("Some error occurred. Check code");
return;// Something went wrong
} else {
console.log("Session : " + sessId);
}
}
Is this good now?
Thanks
Ajay
That's because the success and error function of the ajax request are executed asynchronously.
To make sure this doesn't happen, you should add a callback to your save function that is called after the success or error functions ran.
classUser.prototype.save = function (callback) {
$.ajax({
type: "POST",
url: "http://myapihost.com:8080/api/1.0/user/",
data: JSON.stringify(this),
dataType: "json",
success: function (apiResponse) {
var currSessionID = apiResponse.sessionId;
this.sessionid= currSessionID;
callback(null, currSessionID);
},
error: function (apiResponse) {
alert("error : " + apiResponse);
this.sessionid= "Error";
callback(apiResponse, null);
}
});
}
Then, when calling the save function, you can do something like this:
User.save(function(error, sessId) {
if(error) {
// Something went wrong
} else {
// Do whatever you need to do
}
});
You should note that this will also run asynchronously. So if you want to work with the session ID, don't do that after the User.save(...) call, but inside the function.
$.ajax() issues an asynchronous call to the url specified in the options object. See the jQuery documentation at http://api.jquery.com/jQuery.ajax/
success is a callback function that is invoked when the call is completed and all the response stream is read by the browser so basically in most of the cases the callback (updating the session id) will execute after you try to retrieve it.
I think what is happening here is that the default ajax call is async which means that the code var currSessionID = User.get_curr_session(); can execute before the success call completes.
You have a couple of options, you can try and update your code to be more async capable, using callbacks or other methods, or specify that you want your ajax call to be synchronous.

manipulating local variable with a callback in JS

here's my code with jquery:
function check_hotel_exists(value, col)
{
var url = base_url + 'ajax/checkuniquehotel';
var response = false;
$.get(url, {hotelname:value}, function(data){
if (data === 'true') response = true;
});
alert((response) ? 'Hotel is Not Existing' : 'Hotel Exists');
return [response, "That hotel already exists"];
}
the value of response does not change.
How do I change the value of response with a callback from the get function? I am expecting the variable data from a server response which is either 'true' or 'false'.
Any help would be appreciated.:)
The value does not change because the ajax callback that is supposed to set response to true executes asynchronously after the check_hotel_exists function exited. A possible workaround would be to execute the ajax request synchronously by setting the async option to false.
$.ajax({
url: base_url + 'ajax/checkuniquehotel',
async: false,
data: { hotelname: value },
success: function(result) {
if (result === 'true') response = true;
}
});
Another workaround is to perform the call asynchronously but in this case all the work has to be done in the success callback, your function no longer need to return a value (for example show an error message if the hotel name already exists in the success callback). IMHO this is a better approach.
All ajax requests are asynchronous, so you can't create a function that returns something based on the result of an ajax call. You should handle the response asynchronously (for example: change a piece of text on the page after the ajax call completed):
function check_hotel_exists(value, col)
{
var url = base_url + 'ajax/checkuniquehotel';
$.get(url, {hotelname:value}, function(data){
var msg = (data === 'true') ? 'Hotel is Not Existing' : 'Hotel Exists';
$("#resultmsg").text(response);
});
}
Try this
function check_hotel_exists(value, col)
{
var url = base_url + 'ajax/checkuniquehotel';
var response = false;
$.get(url, {hotelname:value}, function(data){
if (data === 'true') response = true;
alert((response) ? 'Hotel is Not Existing' : 'Hotel Exists');
//this wont work, so do something else!!!!
//return [response, "That hotel already exists"];
});
}
A solution would be to make your check_hotel_exists function asynchronous in its turn, i.e. receiving a function as a callback parameter:
function check_hotel_exists(value, col, callback)
{
var url = base_url + 'ajax/checkuniquehotel';
var response = false;
$.get(url, {hotelname:value}, function(data){
if (data === 'true') response = true;
if (callback) {callback.call(response);}
});
}
This way you can call it wherever you want to check the presence of a hotel like this:
check_hotel_exists(value, col, function(response){
alert(response);
});
Dirty approach, but it works:
Have your variable defined outside callback function:
var response = false;
function check_hotel_exists(value, col)
{
var url = base_url + 'ajax/checkuniquehotel';
response = false;
$.get(url, {hotelname:value}, function(data){
if (data === 'true') response = true;
});
alert((response) ? 'Hotel is Not Existing' : 'Hotel Exists');
return [response, "That hotel already exists"];
}

Categories

Resources