Turn XMLhttpRequest into a function fails: asynchronity or other? - javascript

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);

Related

How to store the results of an async (AJAX) function in a variable?

This is not a duplicate question.
It is a request help finding technical conclusion, that is not addressed in
How do I return the response from an asynchronous call?
If .then() is not resolving the Promise, how do I track down the problem at that point? It seems like I missed something technically, not conceptually.
This function retrieves data from a URL:
async function getINFO(source, callback) {
var xhr = new XMLHttpRequest();
xhr.open("GET", source);
xhr.responseType = "document";
xhr.onreadystatechange = function () {
if (xhr.readyState === 4 && xhr.status === 200){
var text = xhr.responseXML.getElementsByTagName('pre')[0].innerHTML;
console.log(text);
callback(text);
}
}
xhr.send();
}
Externally defined callback:
function asyn_return(input){
console.log(input);
return input;
}
The data comes back perfectly in AJAX.
I want to populate an attribute member (datastore.info) with the data.
info is a URL string.
This is the place in the code where I use my AJAX data retrieval function:
if (typeof(info) === 'string'){
// If I have a URL retrieve the data and add it to the datastore object.
datastore.info = getINFO(info, asyn_return).then(data =>{
console.log(data);
return data;
});
//console.log(datastore.info);
} else {
datastore.info = "";
}
console.log(datastore.info);
console.log(data) returns the data I expect, but datastore.info is not populated:
{item1: "data", item2:"data", info: {} }
Not sure what I'm missing technically to have this resolve to the result.
Once I have the promise (the wrapper), what triggers it to resolve?
And when that is not happening, what is the likely issue?
Thanks to anyone who can help.
Your console.log at the end is assuming that the data comes un a sync manner, but its is not, therefore it is undefined, cause the ajax data is not arrived yet.
You will need to change your code to be an async one, it can be rewritten via promises or async/await.
It can be written like that:
function getINFO(source) {
return new Promise(resolve => {
var xhr = new XMLHttpRequest();
xhr.open('GET', source);
xhr.responseType = 'document';
xhr.onreadystatechange = function() {
if (xhr.readyState === 4 && xhr.status === 200) {
var text = xhr.responseXML.getElementsByTagName('pre')[0].innerHTML;
console.log(text);
resolve(text);
}
};
xhr.send();
});
}
if (typeof info === 'string') {
// If I have a URL retrieve the data and add it to the datastore object.
getINFO(info).then(data => {
console.log(data);
datastore.info = data;
// continue your async code here
});
//console.log(datastore.info);
} else {
datastore.info = '';
}

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.

load multi json data

var address = [ "data/somedata1.json", "data/somedata2.json", "data/somedata3.json", "data/somedata4.json", "data/somedata5.json"];
and function to import this file
function readData()
{
var loadFile = function (filePath, done)
{
var xhr = new XMLHttpRequest();
xhr.open("GET", filePath, true);
xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
xhr.onload = function () { return done(this.responseText) }
xhr.send();
}
address.forEach(function (file, i)
{
loadFile(file, function (responseText)
{
jsonData[i] = JSON.parse(responseText);
if(i === 4)
{
fill(jsonData);
document.getElementById("el").innerHTML = jsonData[2].title3;
Dosometing(jsonData[0])
}
})
})
}
All JSON files have absolute 150kb. Problem is, sometimes when I run this code on website I get jsonData[0] undefinded and sometimes all load success. It means all data are not load properly. What im doing wrong ? There is any chance to write this code better to make sure all files are loaded properly ?
One issue is that even for small files it is not guaranteed, that the downloads finish in order.
It would be better to keep track of the finished download count with a separate variable:
function readData() {
var loadFile = function(filePath, done) {
var xhr = new XMLHttpRequest();
xhr.open("GET", filePath, true);
xhr.setRequestHeader("X-Requested-With", "XMLHttpRequest");
xhr.onload = function() { return done(this.responseText) }
xhr.send();
}
var finishedCount = 0;
address.forEach(function(file, i) {
loadFile(file, function(responseText) {
jsonData[i] = JSON.parse(responseText);
finishedCount++;
if(finishedCount === address.length) {
fill(jsonData[4]);
document.getElementById("el").innerHTML = jsonData[2].title3;
Dosometing(jsonData[0])
}
});
})
}

Perform task only after getting "XmlHttpRequest" response

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();
});

AJAX function to get JSON data

I made a script to get JSON data from a file on our server using AJAX but I'm having trouble putting it into a function.
Here's my code:
function getJSON (file)
{
var request = AjaxRequest();
var json = "";
request.onreadystatechange = function ()
{
if(request.readyState==4 && request.status==200)
{
json = JSON.parse(request.responseText);
}
}
request.open("GET", file, false);
request.send();
return json;
}
The function does everything I want it to but I've been told to NEVER pass false to the AJAX request because of blocking. Something just seems wrong about this function but I have no idea how to change it. Should I change it? If so, how?
You can't return it like this, it's an asynchronous operation, meaning your json = JSON.parse(request.responseText) happens later, when the server responds with data...long after you returned.. Instead you can pass in a function which accepts the data, like this:
function getJSON (file, callback)
{
var request = AjaxRequest();
request.onreadystatechange = function ()
{
if(request.readyState==4 && request.status==200)
{
callback(JSON.parse(request.responseText));
}
}
request.open("GET", file, false);
request.send();
}
Then you call it like this:
getJSON("something.json", function(data) {
//use data, your json object/data
});
This way you're using the data when it's available, passing it onto the next function...this is the way asynchronous calls are intended to behave.

Categories