I have two slightly different versions trying to accomplish the same thing.
In the 2nd example, you are console.logging the result of your loadJSON function. It’s asynchronous and doesn’t return anything. When the console.log line is called, the loadJSON function has returned, (but not the async request yet).
In order to console.log your JSON data, you must place the console.log statement inside a function which can see that data (according to scoping rules).
The first example works because you are console.logging inside of a callback function executed after the async function has returned and into which the data loaded by the async function is passed - it has access to the data.
actual( xobj.responseText )
As the variable callback in loadJSON points to the function actual.
console.log(loadJSON('http://ip-api.com/json',actual)); prints the result from loadJSON, but the function loadJSON doesn’t return anything. Besides, you probably would want to return the data from the AJAX request which would be async and thus not work with console.log(loadJOSN), because it doesn’t return anything (even if you added something like return xobj.responseText) synchronously.
Note that if you made the actual function in v2 the same as in v1, you will see that the AJAX request is still successfull.
Thanks to @michealhall and @BenGitter for your responses. The console.log stuff I was doing was just to make sure I could see what was going on.
Still not getting what I want. Below is another version with descriptions of what I think/want each function to do. See if you can tell me how to think about modifying my functions to accomplish the following. I ditched the callback function to see if it would do what I want
on page load, run the init function
init runs loadJSON with the ip-api.com url and assigns the result to const data and then print the object to the console.
What I expect is for both console.logs to print the same thing. I want the const data in init function to get the JSON object. How do I make this happen? Ultimately, I will be making to separate AJAX calls (one using the ip-api.com and then using longitude and latitude of that returned object, I will then make another AJAX call to get weather data from another url. I only want one function that gets the obj so I can make the 2 different calls to get 2 different results.
I am having trouble visualizing what is happening when my code executes.
The same exact thing applies though - regardless of whether or not you are using console.log.
loadJSON does not return a value - it cannot. It returns and execution of remaining code continues before data ever comes back from the server. That’s what makes the request “asynchronous”. When you try to assign the result of loadJSON to a variable outside of it, you get undefined, because that’s what loadJSON returns.
You need to use a callback to make use of the data returned (no way around it in this instance). Inside of that callback, you need to assign the returned data to a variable or as a property on an object which other functions can see outside of the scope of the request.
However, the gotcha is that those functions still will not be able to see the value until it is returned after the asynchronous request is completed. This means that they cannot run until after the request is finished, if they rely on the data.
So you either need to perform all handling that relies on the data inside the callback (the callback can call other functions, passing the data around as needed), switch to using promises (which is still largely the same pattern as a callback) or use an event based system whereby your callback triggers an event when the asynchronous request is completed and other components in your system listen for that event and take action when they receive it.
I suppose, if you are using a front-end framework, by getting data from the server and updating your data store in memory, they would take care of re-rendering components for you, but in all honesty - even though I author a fairly large project with tons of data and UI - I’ve never used a front end framework for anything, so I have no experience there.
I posted a code sample some time back here (this is one of the the most common questions on the forums) :
It won’t give anything away - it’s just a pattern I use. You’ll still be free to figure things out for yourself.
| Value | State | Description |
| 0 | UNSENT | Client has been created. open() not called yet. |
| 1 | OPENED | open() has been called. |
| 2 | HEADERS_RECEIVED | send() has been called, and headers and status are available. |
| 3 | LOADING | Downloading; responseText holds partial data. |
| 4 | DONE | The operation is complete. |
if (xobj.readyState == 4 && xobj.status == "200")
As you might’ve guessed now, we are interested in xobj.readyState === 4; which means the operation is complete. Here you’re checking for the readyState Number 4 every time the onreadystatechange is called. But what about xobj.status == "200"?
200 is an “OK” HTTP status. It means the request has succeeded. so combining that with xobj.readyState == 4 You can be certain that your XHR request is all set and done.
const parsed = JSON.parse(xobj.responseText);
The information returned from this GET request is returned as text. So we used the JSON.parse to turn it into a usable JSON object.
The send() method initiates the request; the real deal. Usually, when the argument is set to NULL it means this is a GET request. The argument is ignored if the request method is GET anyways.
You create an XHR object. Sets the request (.open()) and Send it afterwards (.send(null)). Then whenever the onreadystatechange event is triggered, you check for (xobj.readyState == 4 && xobj.status == "200") and if they’re both true you do whatever you want with your request.
This is used to force a stream to be treated and parsed as whatever MIME type you set (application/json).
It is used to prevent parsing errors. Read more…
So that’s pretty much it.
Here is the official specifications for XHR. It can be is pretty confusing, but it’s good to have.
Well, I am not finished with the project, but I got something working. I did not look at your page (@michealhall) until I came up with my solution. I am not finished yet, because I still need to do the unit swap functionality and make it visually more appealing. I will now also experiment with Promises, to see if I can make my code better.
Thanks to everyone who replied with such detail. I learned something new today, which is always a good thing.