Use ajax data in multiple functions - javascript

I have an ajax call that looks like this. My question is, with the callback function I can get the data out from myAjaxCall() to another function so I can work on the data. But is it possible to do multiple callbacks, so I can pass the data to two, three or four different functions that do all different things to the data?
Or do I have to do multiple ajax request for every function that I want to use?
function myAjaxCall() {
var xhr = new XMLHttpRequest();
xhr.open('GET', 'myJSON.json', true);
xhr.onload = function() {
if(this.status === 200) {
var data = JSON.parse(this.responseText);
callback && callback(data);
}
};
xhr.send();
}

You can just wrap the callbacks in another function and use that wrapper function instead:
function callback(data) {
callbackOne(data);
callbackTwo(data);
callbackThree(data);
. . .
}
If you need broader use of data than this, you'd need to resolve a Promise or something similar to get the data into a broader scope.

why not just call different functions with data as a parameter, sth. like this:
function myAjaxCall() {
var xhr = new XMLHttpRequest();
xhr.open('GET', 'myJSON.json', true);
xhr.onload = function() {
if(this.status === 200) {
var data = JSON.parse(this.responseText);
functionA(data);
functionB(data);
functionC(data);
}
};
xhr.send();
}
and inside your function do with data whatever you want to do:
functionA(data) {
// TODO do sth. with data
}

Another way could be this:
function myAjaxCall(callbacks) {
var xhr = new XMLHttpRequest();
xhr.open('GET', 'myJSON.json', true);
xhr.onload = function() {
if(this.status === 200) {
var data = JSON.parse(this.responseText);
callbacks && callbacks.forEach(function(callback) {
callback(data);
})
}
};
xhr.send();
}
// Where callback1 and callback2 are both functions
myAjaxCall([callback1, callback2]);
Note that I've changed the way of using myAjaxCall.
Also, if you don't mind using ES6, this would be a great option:
function myAjaxCall(...callbacks) {
var xhr = new XMLHttpRequest();
xhr.open('GET', 'myJSON.json', true);
xhr.onload = function() {
if(this.status === 200) {
var data = JSON.parse(this.responseText);
callbacks.length && callbacks.forEach(callback => callback(data));
}
};
xhr.send();
}
// Where callback1 and callback2 are both functions
myAjaxCall(callback1, callback2);
Note that in both cases I'm passing the callbacks to the ajax function, so no matter where that function is placed, it will have the callbacks available (I'm talking about scope). It's just not a good practice to define a function that uses user defined variables outside it's own scope.

Related

How to separate XMLHttpRequest from the main function for better visbility/testibility (without Promises / asnyc/await )

Imagine this function:
function myMainFunction() {
doSomeInitialStuff();
// more stuff..
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
if (xhr.readyState == XMLHttpRequest.DONE) {
// Now that we know we received the result, we can do the heavy lifting here
if (xhr.status == 200) {
console.log("ready 200");
let result = JSON.parse(xhr.responseText);
doStuff(result);
// and much more stuff..
} else {
console.log("error", xhr.status);
return undefined;
}
}
};
xhr.open("GET", "http://example.com", true);
xhr.send(null);
}
This works fine, but it is impossible to test, and this function has become a monster.
So I'd like to refactor it, by separating all the different parts in their own unique functions.
The problem is, I do not know how to extract the XHR part and still keep it working.
I cannot use Promises nor asnyc/await and have to stick to using plain XHR.
What I'd normally do is to create a seperate async function for the ajax call (or the xhr in this case). Simply await it's result and go from there. Easy to separate. But I do not have the luxury of await or anything this time.
What I am trying to get at is something like this
function refactoredMyMainFunction() {
doSomeInitialStuff();
// more stuff..
let result = xhrFunction();
doStuff(result); // result would be undefined here, since I cannot wait for the xhr request to finish.
}
You can implement a callback-based API:
function myMainFunction() {
doSomeInitialStuff();
// more stuff..
xhrFunction(doStuff);
}
function xhrFunction(cb) {
var xhr = new XMLHttpRequest();
xhr.onreadystatechange = function () {
if (xhr.readyState == XMLHttpRequest.DONE) {
// Now that we know we received the result, we can do the heavy lifting here
if (xhr.status == 200) {
console.log("ready 200");
let result = JSON.parse(xhr.responseText);
cb(result);
// and much more stuff..
} else {
console.log("error", xhr.status);
return undefined;
}
}
};
xhr.open("GET", "http://example.com", true);
xhr.send(null);
}

Vanilla JavaScript async multiple xhr.open()

I would like know how to handle multiple xhr requests with vanilla JS. I want to open multiple html templates and load the page when all of them are ready. when I use few xhr.open() requests it will only return 1 template:
var xhr = new XMLHttpRequest();
xhr.onload = function() {
if(xhr.status === 200){
storage.append(xhr.responseText);
}
}
function getAsync(url) {
xhr.open('get', url);
xhr.send();
}
getAsync('localhost:3000/template1.html');
getAsync('localhost:3000/template2.html');
I understand that .open() only works 1 at a time.
So is it possible to load all teamplates asynchronously or should I just load one after another in a synchronous matter? Also, I wonder,if I should create multiple xhr = new XMLHttpRequest() objects so that I could run multiple .open()?
Thank you
You are using one variable to define the xhr request, and using that variable twice, thus overriding the variable the second time. You need to create a loop and use let xhr; instead of var xhr as let has a block scope, so each instance in the loop will be independently defined.
i.e. something like
// Create iterable list of URLS
let urls = ['localhost:3000/template1.html', 'localhost:3000/template2.html'];
// Loop through URLs and perform request
for(let i=0; i<urls.length; i++) {
let xhr = new XMLHttpRequest();
xhr.onload = function() {
if(xhr.status === 200){
storage.append(xhr.responseText);
}
}
xhr.open('get', urls[i]);
xhr.send();
}

Context problems with XMLHttpRequest.onreadystatechange

Background
I am making a request every 5 seconds using XMLHttpRequest and I want to print my name when I receive the response.
To do this I am using onreadystatechange which allows me to define a callback function when I receive the answer.
Problem
To achieve this, I am using a class. When I first initiate the class I say my name immediately, and then I start a process using setTimeInterval to make a request every 5 seconds and see my name.
The problem is that I see my name the first time, but then I never see it again. The issue is that this seems to be in different context, and thus this.sayMyName() doesn't exist because it doesn't belong to the xhr object.
What I tried
To fix this I tried using scoping by following another StackOverflow question but unfortunately this remains undefined.
Code
class Cook {
constructor() {
// innitial call so we don't have to wait
//5 seconds until we see my name
this.getCookInfo();
setInterval(this.getCookInfo, 5000);
}
getCookInfo() {
var xhr = new XMLHttpRequest(),
method = "GET",
url = "https://best.cooks.in.the.world.org/";
xhr.open(method, url, true);
//Call a function when the state changes.
xhr.onreadystatechange = (self => {
return () => {
if (xhr.readyState == XMLHttpRequest.DONE && xhr.status == 200)
self.sayMyName();
};
})(this);
}
sayMyName() {
console.log("Heisenberg");
}
}
Questions:
Is there a way to fix this code without have to pass a context object to the setInterval function?
Note
Kudos++ for those of you who get my reference :P
bind the this.getCookInfo function to this
then you can rally simplify your code
class Cook {
constructor() {
// innitial call so we don't have to wait
//5 seconds until we see my name
this.getCookInfo();
setInterval(this.getCookInfo.bind(this), 5000);
}
getCookInfo() {
var xhr = new XMLHttpRequest(),
method = "GET",
url = "https://best.cooks.in.the.world.org/";
xhr.open(method, url, true);
//Call a function when the state changes.
// no need for self gymnastics any more
// using onload, no need to test reasyState either
xhr.onload = e => {
if (xhr.status == 200)
this.sayMyName();
};
// your code lacks one thing
xhr.send();
}
sayMyName() {
console.log("Heisenberg");
}
}
An alternative -
class Cook {
constructor() {
this.getCookInfo();
}
getCookInfo() {
var getit = () => {
var xhr = new XMLHttpRequest(),
method = "GET",
url = "https://best.cooks.in.the.world.org/";
xhr.open(method, url, true);
//Call a function when the state changes.
xhr.onload = e => {
if (xhr.readyState == XMLHttpRequest.DONE && xhr.status == 200)
this.sayMyName();
};
xhr.send();
};
getit();
setInterval(getit, 5000);
}
sayMyName() {
console.log("Heisenberg");
}
}
I'm only 99% sure this is right though :p

Accessing data returned by ajax request on load

I've been working on a single page site in which data from a single json file is rendered variously in different sections - nothing displayed on load, but only upon click event. I learned a little about callbacks getting that wired up, but as I almost completed it I realized how flawed the concept was, so now I'm back to the drawing board.
My idea now is to make the ajax call onload, set the json result as a variable available to several functions. I thought it would go something like this:
window.onload = function() {
var myJsonData = function() {
var request = new XMLHttpRequest();
request.open("GET", "/json/someJsonData.json", true);
request.setRequestHeader("content-type", "application/json");
request.send(null);
request.onreadystatechange = function() {
if (request.readyState === 4) {
if (request.status === 200) {
var myJsonString = JSON.parse(request.responseText);
var myJsonArray = myJsonString["Projects"];
return myJsonArray;
}
}
} // onreadystatechange
} // var myJsonData
myJsonData(); // *
console.log("myJsonArray: " + myJsonArray); // undefined
Or:
window.onload = function() {
myJsonData();
}
function myJsonData() {
var request = new XMLHttpRequest();
request.open("GET", "/json/someJsonData.json", true);
request.setRequestHeader("content-type", "application/json");
request.send(null);
request.onreadystatechange = function() {
if (request.readyState === 4) {
if (request.status === 200) {
var myJsonString = JSON.parse(request.responseText);
var myJsonArray = myJsonString["Projects"];
alert("you are here (myJsonArray defined)");
alert("myJsonArray: " + myJsonArray);
console.log("myJsonArray: " + myJsonArray);
return myJsonArray;
}
}
} // onreadystatechange
} // var myJsonData
console.log("myJsonArray: " + myJsonArray); // undefined
Or:
window.onload = myJsonData;
// etc.
Of course myJsonArray is undefined either way. I'm obviously missing something fundamental about how to do this (or is this even a bad idea altogether?). Is there some way to pass a result as a callback when invoking an ajax request on load?
Can someone please enlighten me as to how to proceed from here, a skeletal example perhaps ? (p.s. still focusing on native js, not jQuery)
Many thanks in advance,
svs
There are some things wrong in the code. First the myJsonArray is local to the function myJsonData(). You have to define it globally. Second, the ajax request is async. You have to wait for the result before working with it. Here is a working example based on your code:
var myJsonArray; //Globally defined
window.onload = function() {
var outputData = function() {
console.log(myJsonArray);
}
var myJsonData = function() {
var request = new XMLHttpRequest();
request.open("GET", "someJsonData.json", true);
request.send(null);
request.onreadystatechange = function() {
if (request.readyState === 4) {
if (request.status === 200) {
myJsonArray = JSON.parse(request.responseText);
outputData(); //Ouput when result is received
}
}
} // onreadystatechange
} // var myJsonData
myJsonData(); // *
}
Using Javascript promises could solve your problem here. Promises are native to ES6, although obviously the browser support isn't quite fully there yet. Regardless, here is how you could implement your code with promises:
window.onload = function () {
myJsonData().then(function (result) {
console.log("myJsonArray: " + result);
alert(result.one);
// do something else here
});
}
function myJsonData() {
return new Promise(function (resolve, reject) {
var request = new XMLHttpRequest();
request.open("GET", "http://echo.jsontest.com/key/value/one/two", true);
request.setRequestHeader("content-type", "application/json");
request.send(null);
request.onreadystatechange = function() {
if (request.readyState === 4) {
if (request.status === 200) {
var myJsonString = JSON.parse(request.responseText);
//var myJsonArray = myJsonString["Projects"];
resolve(myJsonString);
}
}
} // onreadystatechange
});
} // var myJsonData
Here is a jsfiddle
From my experience and understanding.
When you are dealing with sync function and async function, always it is important to know which functions require data from the async callback function.
If the functions fully depend on the data received, it will a good coding practice to call these function in the loop
if (request.readyState === 4) {
if (request.status === 200) {
var myJsonString = JSON.parse(request.responseText);
// call the function which depend on the data received here.
}
}
I wouldn't recommend setting the async as false as it would just result in page freeze sometimes.

function to return string in javascript

I am trying to call an ajax request to my server for json data using a function. If I console out the resp variable inside the ajax function it will show the data successfully. If i try to set the ajax function to a variable, and then console that variable it returns undefined. Any ideas who to make the function request the data and then set ti to a variable to be consoled?
function jsonData(URL) {
var xhr = new XMLHttpRequest();
xhr.open("GET", URL, true);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
var resp = JSON.parse(xhr.responseText);
return resp;
}
}
xhr.send();
}
jsonString = jsonData(http://mywebsite.com/test.php?data=test);
console.log(jsonString);
This is actually pretty simple.. Change your call to by synchronous..
xhr.open("GET", URL, false);
That being said this will block the browser until the operation has been completed and if you can use a callback instead it would likely be preferred.
function jsonData(URL, cb) {
var xhr = new XMLHttpRequest();
xhr.open("GET", URL, true);
xhr.onreadystatechange = function() {
if (xhr.readyState == 4) {
var resp = JSON.parse(xhr.responseText);
cb(resp);
}
}
xhr.send();
}
jsonData("http://mywebsite.com/test.php?data=test"
, function(data) { console.log(data); });

Categories

Resources