Return result from AJAX - javascript

I want to return response data when XMLHttpRequest transaction completes successfully.
upon calling my function trashAnswer() to a variable I expect to assign the value returned to the variable and do something with it like below:
I can see resData is always undefined when i try to access data from it.
Please how can I solve this?
var resData = trashAnswer({answerid:58,answererid:65,questionid:458});
This is the code:
//This function will trash answer object
function trashAnswer(param){
if (typeof param !== 'object' || param == null ) {
throw "trashAnswer(): Strictly expect valid object.";
}
var param = $.extend({answerid:0,answererid:0},param),
fd = new FormData();
fd.append('answerData',JSON.stringify(param));
fd.append('trash-answer',true);
var req = AJAX_REQEUST_OB();
req.open(bigPipe.formMethod.a,ajax.ac,true);
req.onload = function(){
if (req.readyState === req.DONE && req.status === 200) {
//ParseJSON is a custom function to check if response is a valid JSON...if its valid then then function will return response Data else return false
var Data = ParseJSON(req.responseText);
//process data...
var EvalData = !Data ? function(){
throw "Invalid JSON";
}:function(Data){
//do something...
return Data;
};
//call EvalData ****method
EvalData(Data);
}
}
//send request.
req.send(fd);
}

This is an asynchronous call. When you do trashAnswer({...}); you are triggering the request and your code continues to execute. At some point in the future your request may work. Here the "may" part is important: your request may fail for several reasons, but you are only checking the HTTP status 200, that is an "OK" response).
An easy solution:
var resData;
trashAnswer({answerid:58,answererid:65,questionid:458});
//This function will trash answer object
function trashAnswer(param){
// ...
req.onload = function(){
if (req.readyState === req.DONE && req.status === 200) {
// ...
// in your "//do something...":
resData = // your result here. If you do not have a local variable,
// it will change the global one (not recommended, see comment below)
}
}
// ...
}
Your solution is even better if you avoid using a global variable (See here why). But I believe this is a good starting point for you to understand how async calls work.
You can also use promises (including the async/await syntax). A good start reading about how this works is https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Using_promises

Related

Make a while loop delay repeating until ajax calls in it are complete

Before I explain what I want to do, here's an MCV of what I'm coding
$("#button").submit(function(event) {
event.preventDefault();
var formData = new FormData(this);
var myString = $('#textarea').val();
var myRegexp = /src="blob:([^'"]+)"/gm;
match = myRegexp.exec(myString);
var inProgress = 0;
while (match != null) {
var xhr = new XMLHttpRequest();
addr = match[1];
xhr.open('GET', 'blob:' + addr, true);
xhr.responseType = 'blob';
xhr.onload = function(e) {
if (this.status == 200) {
var myBlob = this.response;
var data = new FormData();
data.append('file', myBlob);
$.ajax({
url: "uploader.php",
type: 'POST',
data: data,
contentType: false,
processData: false,
beforeSend: function() {
inProgress++;
},
success: function(data) {
myString = myString.replace("blob:" + addr, data);
},
error: function() {
// error
},
complete: function() {
--inProgress;
}
});
} else {
// error
}
};
xhr.send();
match = myRegexp.exec(myString);
}
if (!inProgress) {
formData.set('textarea', myString);
submitForm(formData);
}
});
So, I have a text area and it contains an unknown number of BLOB objects. I first try to find these BLOB objects using regexp and then I upload them to the server using a PHP file called uploader.php. Once the file is uploaded, it will return the URL of the uploaded file and I want to replace the BLOB URL by the URL of the uploaded file in the text area and then submit the final content of the textarea to the server for further processing.
It turns out that when I run the code, only the last instance of the regexp is replaced by its uploaded URL. The others remain as they were. I suspect that this is because the while loop does not wait for the ajax requests to be complete. I had a similar problem when trying to submit the form and I solved it by following the suggestions in this answer but I don't know how to fix this issue this time.
Any idea is appreciated
Update: I tried to make ajax work synchronously but my browser said that it was deprecated and it didn't work.
It seems you don't really need it to be synchronous (and I can't see a case when it's better to make an async action look synchronous), but rather only need it to be sequential.
It is possible to make async actions sequential by the use of callbacks (which are rewritable as Promise and in turn rewritable as async/await methods but I'll keep it simple):
// myString is made global for simplicity
var myString;
function uploadBlob(myBlob, addr, callback) {
var data = new FormData();
data.append('file', myBlob);
$.ajax({
url: "uploader.php",
type: 'POST',
data: data,
contentType: false,
processData: false,
success: function(data) {
// file uploaded OK, replace the blob expr by the uploaded file url(data)
myString = myString.replace("blob:" + addr, data);
callback();
},
error: function() {
// error, the uploaded most likely failed, we leave myString alone
// or alternatively replace the blob expr by empty string
// because maybe we dont want to post blob in the final form submit
// uncomment if needed
// myString = myString.replace("blob:" + addr, "");
callback();
}
});
}
function getBlobAndUpload(addr, callback) {
var xhr = new XMLHttpRequest();
xhr.open('GET', 'blob:' + addr, true);
xhr.responseType = 'blob';
xhr.onload = function(e) {
if (this.status == 200) {
var myBlob = this.response;
uploadBlob(myBlob, addr, callback);
} else {
// error, but callback anyway to continue processing
callback();
}
};
xhr.send();
}
function processAddresses(addresses, callback, current) {
var index = current || 0;
// all addresses processed?
if (index >= addresses.length) {
// yes no more address, call the callback function
callback();
} else {
var addr = addresses[index];
// once the get/upload is done the next address will be processed
getBlobAndUpload(addr, function() {
processAddresses(addresses, callback, index + 1);
});
}
}
$("#button").submit(function(event) {
event.preventDefault();
var formData = new FormData(this);
var addresses = [];
// initialize both localString and myString to the content of the textArea
// localString will be used to extract addresses,
// while myString will be mutated during the upload process
var localString = myString = $('#textarea').val();
var myRegexp = /src="blob:([^'"]+)"/gm;
match = myRegexp.exec(localString);
// collect all addresses first
while (match != null) {
addr = match[1];
addresses.push(addr);
match = myRegexp.exec(localString);
}
// initiate sequential processing of all addresses, and
// pass the callback function triggering the form submit
processAddresses(addresses, function() {
// all the successfully uploaded blob exprs in my string should
// be now replaced by the remote file url now (see commented part
// in upload blob error for a variation of the feature
formData.set('textarea', myString);
submitForm(formData);
});
});
So. I said in comments, that you could use async/await, and gave links. Now I am going to try to teach you how to work with promises and XMLHttpRequest.
So first thing. I would use my own 'library' (not really a library, just 3 new command) called PromiseReq which has XMLHttpsRequest that returns Promises.
You would need two functions from it:
sendToServer(config, data) and getServerFile(config). They do what their names implies.(sendToServer is not so good at the time, but I will improve it sometime later). They just use Promises as returns. They work in very easy way. Code # Github
BUT It was designed for my uses only, so it is not very flexible (although I hope I will improve it sometime).
So we need to learn how to use Promises.
Firstly you need to know what Promise is and why do we use it.
Then you can create one like this:
let pmq = new Promise((res,rej)=>{
// PROMISE BODY HERE
});
Here first warning. Promises made that way don't support return as a way to resolve Promise! You have to use res()!
Some functions just return them (such as fetch()) and we can handle them right after calling function.
Now pmq will be our promise.
You can use pmq.then(callback) to handle what will happen if somewhere in promise body is res() call and pmq.catch(callback) to handle what happens when rej() is called. Remember, that .catch(cb) and .then(cb) returns a Promise, so you can safely chain more than one .then() and at the end add .catch() and it will handle rejection from every one of .then()s.
For example:
pmq = fetch("file.txt");
pmq.then(e=>console.log(e.json())).then(console.log).catch(console.error);
There is a big note there.
The order in which this events will fire.
If for example rP() waits 1s than logs "A" then resolves, this code:
let a = rP();
a.then(_=>console.log("B")).catch(console.error);
console.log("C");
will result in:
C
A
B
Becuase of this there is async/await needed to do this.
To do so you have to make an async function with keyword async.
let fn = async ()=>{}
Here is second thing. Async functions ALWAYS return Promise. And that is the second way you can create a promise. You just don't use res(), rej() only return and throw.
Now we can call inside fn():
let a = await rP().then(_=>console.log("B")).catch(console.error);
console.log("C");
and we will get our
A
B
C
Now. How to use it with XMLHttpRequest?
You need to create new Promise with simple XMLHttpRequest inside:
let xmlp = (type, path,data) => return new Promise((res,req)=>{
let xhr = new XMLHttpsRequest();
xhr.open(type, path, true); // true implies that is it asynchronous call
//...
xhr.send(data);
});
And now when to resolve?
XMLHttpRequest has useful event properties and events. The one that is best for our case is onreadystatechange.
You can use it like so:
xhr.onreadystatechange = _=>{
if(xhr.readyState === 4 && xhr.status === 200) // Everything went smoothly
res(xhr.responseText);
else if(shr.readyState === 4 && xhr.status !== 200) // Something went wrong!
rej(xhr.status);
}
And then to get data you can either
Async/Await
// INSIDE ASYNC FUNCTION
let resData = await xmpl("GET", "path.txt", null).catch(console.error);
.then()
let resData;
xmpl("GET", "path.txt", null).then(r=>{
resData = r;
// REST OF WHOLE FUNCTION TO ENSURE THAT resData HAS BEEN SET
})
.catch(console.error);
You can also send data with xmpl().
xmpl("POST", "observer.php", "Data to send to observer.php!")
.then(whatToDoAfterSendFN);
/*
to get such data in PHP you have to use
$post = file_get_contents('php://input');
*/
I know that this answer is a bit messy and stuff, but I didn't have any idea how to write it :P Sorry.

How to call a callback method after HTTP request

My code is running inside a main function. One part of my code is to make an HTTP request with an parameter which was defined before in the function and than write the response in to a new variable and work with it later.
I would like to exclude these steps with HTTP Request outside of the main function, and just CALL the function and write the response in a variable.
Unfortunately I tried it, but it doesn't work.
Error: variable is undefined
My code:
function DoWork() {
//some code
var strResponseHttpRequest;
strResponseHttpRequest = HttpRequest(strInput, function(strInput) {
console.log(strInput);
};
//continue working with the variable 'strResponseHttpRequest'
//rest of my code
}
function HttpRequest(strInput, callBackMethod) {
var objRequest = new XMLHttpRequest(); //New request object
objRequest.onreadystatechange = function() {
// Waits for correct readyState && status
if (objRequest.readyState == 4 && objRequest.status == 200) callBackMethod(objRequest.responseText)
}
objRequest.open("get", "php/get-content.php?ID=" + strInput, true);
objRequest.send();
}
I hope you can help me to find out, where the issue is. If there are some better way to do this, let me know. I would be appreciate it. Thank you.
Do you mean an asynchronous callback? You'll want to wait until the server gives back the correct status and readyState.
Change this:
objRequest.onload = function() {
var strResponse = this.responseText;
return strResponse;
};
To this:
objRequest.onreadystatechange = function() {
// Waits for correct readyState && status
if (objRequest.readyState == 4 && objRequest.status == 200) callBackMethod(objRequest.responseText);
};`
and pass in a callback method as a second parameter to HttpRequest(strInput, callbackMethod) like so:
strResponseHttpRequest = HttpRequest(strInput, function(responseText) {
console.log(responseText);
});
Also add callbackMethod as a parameter like so:
HttpRequest(strInput, callbackMethod)
Where is your strInput variable in your DoWork() function coming from? Maybe you forgot to define it somewhere? Could be for example:
function DoWork(strInput) {
//some code
var strResponseHttpRequest;
strResponseHttpRequest = HttpRequest(strInput);
console.log(strResponseHttpRequest);
//continue working with the variable 'strResponseHttpRequest'
//rest of my code
}
function HttpRequest(strInput) {
function reqListener () {
console.log(this.responseText);
}
var objRequest = new XMLHttpRequest(); //New request object
objRequest.onload = function() {
var strResponse = this.responseText;
return strResponse;
};
objRequest.open("get", "php/get-content.php?ID=" + strInput, true);
objRequest.send();
}
DoWork("yourIDvalue");

XMLHttpRequest send() from local file returns undefined with Workers

I got a problem by handling with workers, when i make my request the XMLHttpRequest() send returns null, but the onload method returns the file which i expected when i call send. What am I doing wrong?
Here's the code:
function pegarDados(){
let requisicao = new XMLHttpRequest();
requisicao.overrideMimeType('text/plain');
requisicao.setRequestHeader('X-Requested-With', 'JSONHttpRequest');
requisicao.open('GET','./assets/data/dados.json', false);
requisicao.onload = function(){
return JSON.parse(this.response);
};
let resultado = requisicao.send();
return resultado.responseText;
}
self.onmessage = function (event) {
if(event.data == 'lista'){
let dados = pegarDados();
console.error(dados);
self.postMessage(dados);
}
}
XMLHttpRequest.send()actually has a return value of undefined.
In your code, you are trying to assign the set the value of your request by doing:
let resultado = requisicao.send();
which would obviously assign undefined to the variable.
What you were doing with xhr.onload is what I would do too, I don't really see why you want to have two return methods.
Looking as you made your xhr not asynchronous, you could just do:
requisciao.send();
return requisciao.responseText;

Return JSON from XMLHttpRequest Function

Hello I've been trying to wrap my head around returning data from a XMLHttpRequest Function. I've tried many different ways but the only thing i can get when i try to output the data to a console from out-side the function i always get 'undefined'. it only works if i do it from inside the function itself.
<script>
var object;
function loadJSON(path, success, error) {
var xhr = new XMLHttpRequest();
var obj1;
xhr.onreadystatechange = function () {
if (xhr.readyState === XMLHttpRequest.DONE) {
if (xhr.status === 200) {
if (success)
success(JSON.parse(xhr.responseText));
//console.log(data); works here!
} else {
if (error)
error(xhr);
}
}
};
xhr.open("GET", path, true);
xhr.send();
}
object = loadJSON('jconfig.json',
function (data) { console.log(data); return($data);/*works here! but does not return!*/ },
function (xhr) { console.error(xhr); }
);
console.log(object);//does not work here
</script>
I know this is a very simple problem but I've been stuck with this problem for over an hour now and the answers given on other similar questions cant seem to get me over this obstacle. Any help is highly appreciated!
EDIT: I updated the code with some suggestions but i still cant get ti to work. Any suggestions to get the code above to finally return something i can use outside of the functions.
The line console.log(object) is executed just after the laodJSON() function is called and the JSON object isn't loaded till then.
This is related to callbacks and async functions. Your loadJSON() can only actually load the JSON when it get's response from the server.
Instead, if you want to call the JSON object outside the loadJSON() function, you need to use a callback function. Something like this:
<script>
var object;
function loadJSON(path, callback) {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
if (xhr.readyState === XMLHttpRequest.DONE) {
if (xhr.status === 200) {
// Here the callback gets implemented
object = JSON.parse(xhr.responseText);
callback();
} else {
}
}
};
xhr.open("GET", path, true);
xhr.send();
return xhr.onreadystatechange();
}
loadJSON('jconfig.json', function printJSONObject(){
console.log(object);
});
// this will not work unless you get the response
console.log(object);
</script>
Update: "Returning" a value from an async function by the use of callbacks is pointless, since the next line of code will be executed immediately without waiting for the response.
Instead, if you want to use the object outside of the function sending an XHR request, implement everything inside your callback function.

Form submit using Ajax

I need to submit the a form using Ajax with POST method.The code is as follows,
function persistPage(divID,url,method){
var scriptId = "inlineScript_" + divID;
var xmlRequest = getXMLHttpRequest();
xmlRequest.open("POST",url,true);
xmlRequest.onreadystatechange = function(){
alert(xmlRequest.readyState + " :" + xmlRequest.status);
if (xmlRequest.readyState ==4 || xmlRequest.status == 200)
document.getElementById(divID).innerHTML=xmlRequest.responseText;
};
xmlRequest.open("POST", url, false);
alert(xmlRequest.readyState);
xmlRequest.send(null);
}
but the form is not submitting(request is not executed or no data posted).How to submit the form using Ajax.
Thanks
There's a few reasons why your code doesn't work. Allow me to break it down and go over the issues one by one. I'll start of with the last (but biggest) problem:
xmlRequest.send(null);
My guess is, you've based your code on a GET example, where the send method is called with null, or even undefined as a parameter (xhr.send()). This is because the url contains the data in a GET request (.php?param1=val1&param2=val2...). When using post, you're going to have to pass the data to the send method.
But Let's not get ahead of ourselves:
function persistPage(divID,url,method)
{
var scriptId = "inlineScript_" + divID;
var xmlRequest = getXMLHttpRequest();//be advised, older IE's don't support this
xmlRequest.open("POST",url,true);
//Set additional headers:
xmlRequest.setRequestHeader('X-Requested-With', 'XMLHttpRequest');//marks ajax request
xmlRequest.setRequestHeader('Content-type', 'application/x-www-form-urlencode');//sending form
The first of the two headers is not always a necessity, but it's better to be safe than sorry, IMO. Now, onward:
xmlRequest.onreadystatechange = function()
{
alert(xmlRequest.readyState + " :" + xmlRequest.status);
if (xmlRequest.readyState ==4 || xmlRequest.status == 200)
document.getElementById(divID).innerHTML=xmlRequest.responseText;
};
This code has a number of issues. You're assigning a method to an object, so there's no need to refer to your object using xmlRequest, though technically valid here, this will break once you move the callback function outside the persistPage function. The xmlRequest variable is local to the function's scope, and cannot be accessed outside it. Besides, as I said before, it's a method: this points to the object directlyYour if statement is a bit weird, too: the readystate must be 4, and status == 200, not or. So:
xmlRequest.onreadystatechange = function()
{
alert(this.readyState + " :" + this.status);
if (this.readyState === 4 && this.status === 200)
{
document.getElementById(divID).innerHTML=this.responseText;
}
};
xmlRequest.open("POST", url, false);
alert(xmlRequest.readyState);//pointless --> ajax is async, so it will alert 0, I think
xmlRequest.send(data);//<-- data goes here
}
How you fill the data is up to you, but make sure the format matches the header: in this case 'content type','x-www-form-urlencode'. Here's a full example of just such a request, it's not exactly a world beater, since I was in the process of ditching jQ in favour of pure JS at the time, but it's serviceable and you might pick up a thing or two. Especially take a closer look at function ajax() definition. In it you'll see a X-browser way to create an xhr-object, and there's a function in there to stringify forms, too
POINTLESS UPDATE:
Just for completeness sake, I'll add a full example:
function getXhr()
{
try
{
return XMLHttpRequest();
}
catch (error)
{
try
{
return new ActiveXObject('Msxml2.XMLHTTP');
}
catch(error)
{
try
{
return new ActiveXObject('Microsoft.XMLHTTP');
}
catch(error)
{
//throw new Error('no Ajax support?');
alert('You have a hopelessly outdated browser');
location.href = 'http://www.mozilla.org/en-US/firefox/';
}
}
}
}
function formalizeObject(form)
{//we'll use this to create our send-data
recursion = recursion || false;
if (typeof form !== 'object')
{
throw new Error('no object provided');
}
var ret = '';
form = form.elements || form;//double check for elements node-list
for (var i=0;i<form.length;i++)
{
if (form[i].type === 'checkbox' || form[i].type === 'radio')
{
if (form[i].checked)
{
ret += (ret.length ? '&' : '') + form[i].name + '=' + form[i].value;
}
continue;
}
ret += (ret.length ? '&' : '') + form[i].name +'='+ form[i].value;
}
return encodeURI(ret);
}
function persistPage(divID,url,method)
{
var scriptId = "inlineScript_" + divID;
var xmlRequest = getXhr();
xmlRequest.open("POST",url,true);
xmlRequest.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
xmlRequest.setRequestHeader('Content-type', 'application/x-www-form-urlencode');
xmlRequest.onreadystatechange = function()
{
alert(this.readyState + " :" + this.status);
if (this.readyState === 4 && this.status === 200)
{
document.getElementById(divID).innerHTML=this.responseText;
}
};
xmlRequest.open("POST", url, false);
alert(xmlRequest.readyState);
xmlRequest.send(formalizeObject(document.getElementById('formId').elements));
}
Just for fun: this code, untested, but should work allright. Though, on each request the persistPage will create a new function object and assign it to the onreadystate event of xmlRequest. You could write this code so that you'll only need to create 1 function. I'm not going into my beloved closures right now (I think you have enough on your plate with this), but it's important to know that functions are objects, and have properties, just like everything else:Replace:
xmlRequest.onreadystatechange = function()
{
alert(this.readyState + " :" + this.status);
if (this.readyState === 4 && this.status === 200)
{
document.getElementById(divID).innerHTML=this.responseText;
}
};
With this:
//inside persistPage function:
xmlRequest.onreadystatechange = formSubmitSuccess;
formSubmitSuccess.divID = divID;//<== assign property to function
//global scope
function formSubmitSuccess()
{
if (this.readyState === 4 && this.status === 200)
{
console.log(this.responseText);
document.getElementById(formSubmitSuccess.divID).innerHTML = this.responseText;
//^^ uses property, set in persistPAge function
}
}
Don't use this though, as async calls could still be running while you're reassigning the property, causing mayhem. If the id is always going to be the same, you can, though (but closures would be even better, then).
Ok, I'll leave it at that
This code can let you understand. The function SendRequest send the request and build the xmlRequest through the GetXMLHttpRequest function
function SendRequest() {
var xmlRequest = GetXMLHttpRequest(),
if(xmlRequest) {
xmlRequest.open("POST", '/urlToPost', true)
xmlRequest.setRequestHeader("connection", "close");
xmlRequest.onreadystatechange = function() {
if (xmlRequest.status == 200) {
// Success
}
else {
// Some errors occured
}
};
xmlRequest.send(null);
}
}
function GetXMLHttpRequest() {
if (navigator.userAgent.indexOf("MSIE") != (-1)) {
var theClass = "Msxml2.XMLHTTP";
if (navigator.appVersion.indexOf("MSIE 5.5") != (-1)) {
theClass = "Microsoft.XMLHTTP";
}
try {
objectXMLHTTP = new ActivexObject(theClass);
return objectXMLHTTP;
}
catch (e) {
alert("Errore: the Activex will not be executed!");
}
}
else if (navigator.userAgent.indexOf("Mozilla") != (-1)) {
objectXMLHTTP = new XMLHttpRequest();
return objectXMLHTTP;
}
else {
alert("!Browser not supported!");
}
}
take a look at this page. In this line: req.send(postData); post data is an array with values that should be posted to server. You have null there. so nothing is posted. You just call request and send no data. In your case you must collect all values from your form, as XMLHTTPRequest is not something that can simply submit form. You must pass all values with JS:
var postData = {};
postData.value1 = document.getElementById("value1id").value;
...
xmlRequest.send(postData);
Where value1 will be available on server like $_POST['value'] (in PHP)
Also there might be a problem with URL or how you are calling persistPage. persistPage code looks ok to me, but maybe I'm missing something. Also you can take a look if you have no errors in console. Press F12 in any browser and find console tab. In FF you may need to install Firebug extention. Also there you will have Network tab with all requests. Open Firebug/Web Inspector(Chrome)/Developer Toolbar(IE) and check if new request is registered in its network tab after you call persistPage.
I found you've invoked the
xmlRequest.open()
method twice, one with async param as true and the other as false. What exactly do you intend to do?
xmlRequest.open("POST", url, true);
...
xmlRequest.open("POST", url, false);
If you want to send asynchronous request, pls pass the param as true.
And also, to use 'POST' method, you'd better send the request header as suggested by Elias,
xmlRequest.setRequestHeader('Content-type', 'application/x-www-form-urlencode');
Otherwise, you may still get unexpected issues.
If you want a synchronous request, actually you may handle the response directly right after you send the request, just like:
xmlRequest.open("POST", url, false);
xmlRequest.send(postData);
// handle response here
document.getElementById(scriptId).innerHTML = xmlRequest.responseText;
Hope this helps.

Categories

Resources