Perform task only after getting "XmlHttpRequest" response - javascript

Hi all,
var xhrDownloadImage = function (url, callback) {
var xhr = new XMLHttpRequest();
xhr.open("GET", url, true);
xhr.responseType = "blob";
xhr.onerror = function(e){console.log("Error: " + e)};
xhr.onabort = function(e){console.log("Abort: " + e)};
xhr.onload = function () {
console.log("onload");
var result;
if (xhr.status === 200) {
// image as blob
result = xhr.response;
} else {
result = null;
}
callback(result);
};
console.log(xhr.send());
};
I am calling the above function in a for loop,each time after executing the function i need to perform another function, say "function a". I use the xhr.response as the input of "function a".
But since it is async, it doesnt wait for the response and continues executing the loop. So i couldn't get the out put correctly.
I tried changing the XMLHttpRequest to sync. But in that case i get the following error:
Uncaught InvalidAccessError: Failed to set the 'responseType' property on 'XMLHttpRequest': the response type can only be changed for asynchronous HTTP requests made from a document.
How can i keep the 'XMLHttpRequest' async and do "function a" exactly after getting the response each time?
Please help,
Thanks.

That's what the callback is for:
xhrDownloadImage(url,a); // <--- this is how you call function "a"
Alternatively, if you have more than one function that you need to run after the download:
xhrDownloadImage(url,function(result){
a(result);
b(result);
some_other_function();
});

Related

How to use the result from a API call for another call?

I hope someone can help me, because i can't get it to work and it's really frustrating.
I have the following code
var getJSON = (url, callback) => {
var xhr = new XMLHttpRequest();
xhr.open('GET', url, true);
xhr.responseType = 'json';
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
callback(xhr.response);
}
};
xhr.send();
};
I want to use it to get a json object from the given url but inside the function I want to use the response to get another json object back.
var func1 = (url) => {
getJSON(url, (data) => {
//Do smth here with the data and then for example
func2('https://someurl.com/api/somefunction/' + data.smth.age);
});
}
Now the problem is that in func2 the data will be null or undefinded and it doesn't work after that.
In addition I have more than 2 functions which try to call getJSON.
I hope someone has a solution.

Populate table as data chunks are received

I have a problem with my code and I'm struggling finding why it doesn't work as expected.
I have an API that returns data async. and I want the frontend side to add that data as soon as it's being received. What I expect is an API that returns, say 200 items, then javascript to load those 200 items to a table, meanwhile the API keeps returning another 200 items, and then javascript appends them to the table, and so on until there is no more data left.
I'm using vanilla Javascript 5, prototype-based MVC pattern. Perhaps I'm not getting something simple or its far more complex than I expected.
resultView.js
//this function gets executed by some other code not relevant
ResultView.prototype.execute = function(serverName, databaseName, query){
var response = resultController.getData(serverName, databaseName, query);
console.log("response: ", response); //prints undefined
response.done(function(data){ // Uncaught TypeError: Cannot read property 'done' of undefined
console.log("response done: ", response); //doesn't even execute
data.forEach(populateTable); //this code should populates the table
});
}
resultController.js
ResultController.prototype.getData = function(serverName, databaseName, query){
return resultModel.getData(serverName, databaseName, query);
};
resultModel.js
ResultModel.prototype.getData = function (serverName, databaseName, query) {
var dataSend = {
//the code that is being sent
};
var result = "";
var xhr = new XMLHttpRequest();
xhr.open("POST", url, true);
xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
xhr.onprogress = function () {
result += xhr.responseText;
if(xhr.readyState == 4){
console.log("return: ", result); //shows the results properly each time they are received
return result; //not sure about this return
}
}
xhr.send(JSON.stringify(dataSend));
};
}
I know the data is being received in the API, and the data is returned properly in the front end, the issue must be how I am trying to handle it.
Currently, the results I am getting on the console.log at resultModel.js are the expected, the problem seems to be when calling it from resultView.js, I guess when the function calls response.done(), but I am unable to fix it.
Anyone knows how can I approach a solution?
Thanks in advance.
EDIT:
Partially thanks to Ionut, I've managed to make the resultView.js return better datas, but I still have the problem at the resultView.js, when I try to use response.done(...) it tells me it can't do done() of undefined, but the data should be able to be returned. This is my code in resultModel.js now, the rest remains unchanged.
resultModel.js
var xhr = new XMLHttpRequest();
console.log("Sending the request...");
xhr.open("POST", urlBase + "QueryResults", true);
xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
xhr.onreadystatechange = function () {
if (xhr.readyState == 4) {
console.log("return: ", xhr.responseText); //data is logged properly
return xhr.responseText; //it should be returned properly
}
};
xhr.send(JSON.stringify(queryRequest));
You should add a callback function to manage the full response.
If you want to implement something like lazy-loading you should request your API to send you batches of a smaller number of items, you process them then request more until you get them all.
Here is a basic http request.
console.log('Sending the request ...');
var xhr = new XMLHttpRequest();
xhr.open('GET', "//ipinfo.io/json", true);
xhr.send();
xhr.onreadystatechange = processRequest;
function processRequest(e) {
console.log('Getting the response ...');
if (xhr.readyState == 4 && xhr.status == 200) {
var response = JSON.parse(xhr.responseText);
console.log('Your ip address is ' + response.ip);
} else {
console.log('Error state=' + xhr.readyState + ', status=' + xhr.status);
}
}

Get Date Header Asyncronously

As the title says, I want to get the Response Header Date value, but I keep getting the following warning :
Synchronous XMLHttpRequest on the main thread is deprecated because of
its detrimental effects to the end user's experience. For more help,
check https://xhr.spec.whatwg.org/.
My code :
function getxmlhttp () {
// although IE supports the XMLHttpRequest object, but it does not work on local files.
var forceActiveX = (window.ActiveXObject && location.protocol === "file:");
if (window.XMLHttpRequest && !forceActiveX) {
return new XMLHttpRequest();
}else {
try {
return new ActiveXObject("Microsoft.XMLHTTP");
} catch(e) {}
}
alert ("Your browser doesn't support XML handling!");
return null;
};
function srvTime(){
xmlHttp = getxmlhttp();
//xmlHttp.open('HEAD',window.location.href.toString(),false);
//need to send this to a non-volitile page
xmlHttp.open('GET',"blank.php",false);
xmlHttp.setRequestHeader("Content-Type", "text/html");
xmlHttp.send(null);
console.log("raw " + xmlHttp.getResponseHeader("Date"));
return xmlHttp.getResponseHeader("Date");
};
When I switch this line:
xmlHttp.open('GET',"blank.php",true);
To be true, the value returns NULL.
So can this be done, or do I have to just live with the warning in the console?
Thank you
As your title states, you must make the request asynchronously. That means you have to issue the request and wait for it to complete to get the information. Something like this should work:
function srvTime(callback) {
xmlHttp = getxmlhttp();
//xmlHttp.open('HEAD',window.location.href.toString(),false);
//need to send this to a non-volitile page
xmlHttp.onreadystatechange = function () {
if (xmlHttp.readyState == 4) { // The operation is complete
console.log("raw " + xmlHttp.getResponseHeader("Date"));
callback(xmlHttp.getResponseHeader("Date"));
xmlHttp = null;
}
};
xmlHttp.open('GET', "blank.php", true);
xmlHttp.setRequestHeader("Content-Type", "text/html");
xmlHttp.send(null);
};
Note that you must change the signature of your srvTime method. You can't return the data from it, the caller must supply a callback function that receives the date once the request completes.
An example of how you would use this function with the new signature is as follows:
srvTime(function (serverDate) {
document.getElementById("clock").innerHTML = "Game Time: " + serverDate;
});

Javascript Ajax frontend call to asp.net C# backend

I am trying to accomplish simple yet seems complicated task. I am trying to make AJAX call from pure JavaScript (front end) to aspx page (back end) without including any asp ajax library aiming no asp page rendering on front end i.e. only html+JS for front end.
So here is how it goes, on the front end this js code will send asynchronous call to asp page with a variable. The variable is derived from text box value.
function handleRequest() {
if (xhr.readyState < 4) {
return; // The response is not available yet , we do nothing
}
if (xhr.status !== 200) {
alert('Error!'); // error HTTP
return;
}
}
function getValue() {
var textVal = document.getElementById("test1").value;
xhr.open('GET', 'WebForm1.aspx?q=' + textVal , true);
xhr.send();
var response = xhr.responseText;
document.getElementById("bdy").innerHTML = response;
}
var btn = document.querySelector("button");
var xhr = new XMLHttpRequest();
var body = document.getElementById("bdy");
xhr.onreadystatechange = handleRequest;
document.getElementById("header").innerHTML = Date();
btn.addEventListener('click', getValue, true);
Now, on the back end asp code which will echo the textbox value with the time stamp from the server.
protected void Page_Load(object sender, EventArgs e)
{
string getRequest = Request.QueryString["q"];
DateTime dt = DateTime.Now;
string responseText = getRequest + dt.ToString();
Response.Write(responseText);
}
Finally, this code works perfect when I make synchronous call i.e.
xhr.open('GET', 'WebForm1.aspx?q=' + temp, false); but fails if I send asynchronous call i.e. xhr.open('GET', 'WebForm1.aspx?q=' + temp, true);
I would really appreciate your help.
Code that uses data obtained by asynchronous operations should be placed in a callback invoked after the data arrives. You already have such callback function - handleRequest:
function handleRequest() {
if (xhr.readyState < 4) {
return; // The response is not available yet , we do nothing
}
if (xhr.status !== 200) {
alert('Error!'); // error HTTP
return;
}
var response = xhr.responseText;
document.getElementById("bdy").innerHTML = response;
}
function getValue() {
var textVal = document.getElementById("test1").value;
xhr.open('GET', 'WebForm1.aspx?q=' + textVal , true);
xhr.send();
}
I found the answer. The problem was that when doing the AJAX synchronous call, the browser would wait for the response from the server before storing the result to the variable response and hence display the results. However, when doing asynchronous call, the browser won't wait for the response and hence response variable will be null and the innerHTML will display nathing.
By adding a delay to the response receiving code line, the code works perfectly. Here is the code.
setTimeout(function () {
var response = xhr.response;
document.getElementById("bdy").innerHTML = response;
}, 50);
Thanks

Turn XMLhttpRequest into a function fails: asynchronity or other?

I try to turn an XMLHttpRequest into a function such
var getImageBase64 = function (url) { // code function
var xhr = new XMLHttpRequest(url);
... // code to load file
... // code to convert data to base64
return wanted_result; // return result of conversion
}
var newData = getImageBase64('http://fiddle.jshell.net/img/logo.png'); // function call
doSomethingWithData($("#hook"), newData); // reinjecting newData in wanted place.
I'am successful to load the file, and to convert to base64. I'am however consistenly failling to get the result as an output :
var getImageBase64 = function (url) {
// 1. Loading file from url:
var xhr = new XMLHttpRequest(url);
xhr.open('GET', url, true); // url is the url of a PNG image.
xhr.responseType = 'arraybuffer';
xhr.onload = function(e) {
if (this.status == 200) { // 2. When loaded, do:
console.log("1:Response?> " + this.response); // print-check xhr response
var imgBase64 = converterEngine(this.response); // converter
}
}
xhr.send();
return xhr.onload(); // <fails> to get imgBase64 value as the function's result.
}
console.log("4>>> " + getImageBase64('http://fiddle.jshell.net/img/logo.png') ) // THIS SHOULD PRINT THE BASE64 CODE (returned resukt of the function getImageBase64)
See Fiddle here.
How to make it works so it return the new data as output ?
Solution: my final implementation is visible here, and on JS: how to load a bitmap image and get its base64 code?.
Asynchronous calls in JavaScript (like xhr) can't return values like regular functions. The common pattern used when writing asynchronous functions is this:
function asyncFunc(param1, param2, callback) {
var result = doSomething();
callback(result);
}
asyncFunc('foo', 'bar', function(result) {
// result is what you want
});
So your example translated looks like this:
var getImageBase64 = function (url, callback) {
var xhr = new XMLHttpRequest(url);
... // code to load file
... // code to convert data to base64
callback(wanted_result);
}
getImageBase64('http://fiddle.jshell.net/img/logo.png', function(newData) {
doSomethingWithData($("#hook"), newData);
});
When you use xhr.onload your actually defining a function for JS to call when it loads, hence the value of xhr.onload is the function not the output of the function. Returning xhr.onload() will call that function and return the output, but your onload function has no return statement and hence no output. Additionally you are calling xhr.onload synchronously as you set up the object, and hence it won't have any data to process.
I suggest you add a callback parameter to your function like so, this will execute when the data has loaded.
function getImageBase64( url, callback) {
// 1. Loading file from url:
var xhr = new XMLHttpRequest(url);
xhr.open('GET', url, true); // url is the url of a PNG image.
xhr.responseType = 'arraybuffer';
xhr.callback = callback;
xhr.onload = function(e) {
if (this.status == 200) { // 2. When loaded, do:
console.log("1:Response?> " + this.response); // print-check xhr response
var imgBase64 = converterEngine(this.response); // converter
this.callback(imgBase64);//execute callback function with data
}
}
xhr.send();
}
Then you would use it like so
var myCallBack = function(data){
alert(data);
};
getImageBase64('http://fiddle.jshell.net/img/logo.png', myCallBack);

Categories