JSON
allows us to share and structure data across the web. It can be read by API
s
and manipulated using any programming language.
It is called JSON
because it very much resembles Objects
in JavaScript
,
as we can see if we visit the Star Wars API website:

We can retrieve this information using cURL, by using the command curl
and then typing in the address of the API
listing:

From here of course, we can add the information we're looking for:

But how can we make this information more readable?
We use the python
command and json.tool
:

But this information is still in Terminal, or its equivalents. How do we actually retrieve this information and make it available
for consumers? We instantiate XMLHttpRequest
(or XHR
) a built in JavaScript Object
which allows us to call
external API
s. It gives us the method to open, send and close connections to API
s.
First, in our .js
script we set a variable
and assign it a new
instance of the XMLHttpRequest
in our HTML
we create a <div>
with id="data"
.
Beneath this entire explanation is a <div>
with that id where the result will be displayed. Click here to go there directly.
Back in the JavaScript
file, we now create a function that checks for changes in the state of our variable
:
To explain the code:
We create a new xhr Object
, we want to run a check.
If the ready state is equal to 4 and the status is 200, then what we want to do is use JavaScript
to do document.getElementByID()
and retrieve our data div.
And then we're going to change the innerHTML to be equal to the response text that comes back from our xhr object.
Now we have our listener in place, we need to open()
a connection to the API
and then send()
for that information:
XML
open("GET", "API link address")
send()
onreadystatechange()
onreadystatechange()
The very first thing that we do on line 1 is to create a new
instance of the XMLHttpRequest Object
.
XML stands for Extensible Markup Language
, which is similar to HTML
in the way it structures its data, and it's a precursor to JSON
.
We use the xhr.open()
method, and the first argument that we parse in is "GET"
.
There are several different methods that we can use to communicate with a web server.
The two that are most often used are "GET"
and "POST"
.
The "GET"
method is used when we're retrieving data from the server.
This is a standard method that a browser users when retrieving a web page.
"POST"
is used when sending data to the server, such as an uploaded file or form data.
Since in this instance we want to retrieve data from the Star Wars API, we use the "GET"
method.
The second argument is the URL
that we want to retrieve, here it's "https://ci-swapi.herokuapp.com/api/"
(we're making a request to the Star Wars API).
We use xhr.send()
to send the request.
readyState == 4
The xhr Object
maintains an internal state as it's completing various parts of our request operation.
"readyState = 4
" means that the operation has been completed.
We can see that in the Mozilla Developer Network, there are five different states:
0: open()
has not been called yet.
1: the open()
method has been called.
2: send()
has been called.
3: the responseText
now has some data in it.
4: the entire operation is complete.
See this in action hereIn our code, we're checking to see that everything has completed.
status == 200
The HTTP status code of 200 means "OK". Other common HTTP status codes can be seen here.
We then use some JavaScript
to getElementById()
from the DOM
and change its innerHTML
to the responseText
that comes back from our xhr Object
.
But the results are still not very readable to humans, and they are a "string
instead of an Object
.
You can see so by opening console. So now we will use the JSON parse()
method to make them so.
We would expect from here to be able to create a variable
and then call that variable
to show the results, but we cannot.
So how do we get the results?
We can create such a variable
and call it from within the xhr.onreadystatechange
function, but that would
mean we would always have to contain our code within that function. This is not ideal. So, we need to find a way to access what is inside
the function and make it available outside of that function.
We can create another (callback
) function and pass the data into it (the new lines of code are indicated with "-->"):
Again, however, we would only be making the data available inside this function. Again, this is not ideal.
So how do we make the data available outside of the function?
We can use either setTimeout()
or callback()
functions.
But first, lets examine what is happening inside the code:
The code below is the same as before, but I've added 1) and 2) console.log
to show the order in which the
code is being read:
We expect that we can attain the results from the function by calling the results here. But the returned result is
undefined
. This is because this piece of code is being run before the function xhr.onreadystatechange()
is being executed.
Here we can see the steps the xhr.onreadystatechange()
function is going through. As mentioned before, the code is executing through
the different states. We can see this in the numbers printed to the console. So, the function xhr.onreadystatechange()
is actually
being called five times.
So, to stop the code from reading the first part (console.log(data)
) first, we can set a setTimeout() function
setTimeout()
:- Callback
The setTimeout()
function allows us to tell our application to stop executing for a period of time, through the use of a second argument. Again, the new code is indicated with "-->" in the code block below:
We added a second argument to the new function setTimeout()
, that being 500
. This tells the function to stop logging to the
console for 500 milliseconds, which effectively gives the xhr.onreadystatechange()
time to execute before the data is logged.
This means we can get rid of the setData()
function and set data = JSON.parse(this.responseText
inside the xhr.onreadystatechange()
function:
However, the problem with setTimeout()
functions is that not all networks run at the same speed. Nowadays, most of people in cities have
fibre-optic connections that run at high speed, but this is not universal - rural areas still use broadband, while some still use dial-up.
So, we need a different, more robust solution, which is the callback()
function:
Since everything in JavaScript
is an Object
, it follows that funtions()
are Objects
. Because of this,
a function()
can be passed to another function()
as an argument
. This means effectively that we can call data from one function()
by calling the other function()
.
So, let's now rewrite our code as a function()
:
And create our callback
function:
Now, we add in the argument and tell the code to execute to the console:
Above, we have added the argument cb
to the function getData()
. We are telling the lower lines of code (getData(function(data){});
to act as the conduit for cb
in the first line of code.
Perhaps a more elegant solution is to write a separate function instead of using getData()
as a function with data
as an argument. To do this, we delete the
getData(function(data)
function and write a new function called printDataToConsole
. Then, beneath, we tell the code to execute with
printDataToConsole
as an argument to the getData()
function:
The entire code block now reads:
But we still cannot see the results on the page. We'll cover that next.