Start Here: Zero to JavaScript — Callback/Fetching data/Working with JSON
Bite size Javascript Lesson you can finish in 3–5 minutes!
Missed the previous lesson on Built-in functions and Class? Go here.
Context: You need to learn JavaScript in Less than 3 Weeks for a job interview
We have all been there. I did Python and Java in college, and during the second job interview of my life, my then-boss asked me:
Can you do any frontend programming? Do you know any Javascript?
I answered honestly that I don’t know any JavaScript.
Ok, read this book and come learn on the job
Before I know it, I have been doing JavaScript for 12 years!
In today’s lesson, I will teach you about Callbacks/Fetching Data/Working with JSON.
Before Jumping In
Every example in this article can be run inside Chrome’s console, which you can access by:
- Open a new Blank Tab
- Right click and select Inspect
- Select the Console Tab, see a little blue > arrow
- Let’s cook.
Build your own Doggy Photo Collage
In Today’s Challenge, we will build a randomly generated Photo Collage with dog photos right in our tab.
Write a function that loads 50 (or more!) dog photos and place them in the browser. When we click on any of the images, make them say Woof!
So, before we can show all of these amazing dogs, we need to figure out:
- Where do I get images of dogs?
- How do I make a single dog picture show up in the tab, and say “Woof!” when clicked?
- How do I do 1 and 2 50 times? Oh wait, I already learned loops in lesson 2. Never mind.
Loading Data
When we use the browser to visit any website, we are effectively sending a web page request to a machine on the internet. Most requests come back with HTML Webpages (like the one you are reading right now). Some address returns other type of responses.
Try loading this url in a new tab: https://api.thedogapi.com/v1/images/search
You should get something like this:
[{"breeds":[{"weight":{"imperial":"44 - 62","metric":"20 - 28"},"height":{"imperial":"17 - 20","metric":"43 - 51"},"id":21,"name":"Australian Cattle Dog","country_code":"AU","bred_for":"Cattle herding, herding trials","breed_group":"Herding","life_span":"12 - 14 years","temperament":"Cautious, Energetic, Loyal, Obedient, Protective, Brave"}],"id":"9AXHhr6LG","url":"https://cdn2.thedogapi.com/images/9AXHhr6LG.jpg","width":1080,"height":1080}]
This looks complicated, but you can make it look prettier with the Chrome console:
> var data = [{"breeds":[{"weight":{"imperial":"44 - 62","metric":"20 - 28"},"height":{"imperial":"17 - 20","metric":"43 - 51"},"id":21,"name":"Australian Cattle Dog","country_code":"AU","bred_for":"Cattle herding, herding trials","breed_group":"Herding","life_span":"12 - 14 years","temperament":"Cautious, Energetic, Loyal, Obedient, Protective, Brave"}],"id":"9AXHhr6LG","url":"https://cdn2.thedogapi.com/images/9AXHhr6LG.jpg","width":1080,"height":1080}]
< undefined
> console.log(JSON.stringify(data, null, 4));
< VM319:1 [
{
"breeds": [
{
"weight": {
"imperial": "44 - 62",
"metric": "20 - 28"
},
"height": {
"imperial": "17 - 20",
"metric": "43 - 51"
},
"id": 21,
"name": "Australian Cattle Dog",
"country_code": "AU",
"bred_for": "Cattle herding, herding trials",
"breed_group": "Herding",
"life_span": "12 - 14 years",
"temperament": "Cautious, Energetic, Loyal, Obedient, Protective, Brave"
}
],
"id": "9AXHhr6LG",
"url": "https://cdn2.thedogapi.com/images/9AXHhr6LG.jpg",
"width": 1080,
"height": 1080
}
]
< undefined
The data returned on that address is JSON, which stands JavaScript Object Notation.
Remember the basic types from lesson 1?
Any data made from basic types is a valid JSON object. The above data looks like an Array of Objects. Each Object has the following fields: breeds/id/url/width/height. We are interested in the url, which can be accessed with the following:
> data[0].url
< "https://cdn2.thedogapi.com/images/9AXHhr6LG.jpg"
We are getting close! We need a built-in function that would do the web-request, and when it comes back, we can use the above line to get the url string value out.
Load Any Website/Api Endpoint with fetch()
Turns out, loading web address is such a common task, JavaScript comes with a built-in function for it. Try running the following to see what it does:
> fetch('https://api.thedogapi.com/v1/images/search')
< Promise {<pending>}> await fetch('https://api.thedogapi.com/v1/images/search')
< Response {type: "cors", url: "https://api.thedogapi.com/v1/images/search", redirected: false, status: 200, ok: true, …}> (await fetch('https://api.thedogapi.com/v1/images/search')).text()
< Promise {<pending>}> await (await fetch('https://api.thedogapi.com/v1/images/search')).text()
< "[{"breeds":[{"weight":{"imperial":"23 - 28","metric":"10 - 13"},"height":{"imperial":"15.5 - 20","metric":"39 - 51"},"id":111,"name":"Finnish Spitz","bred_for":"Hunting birds, small mammals","breed_group":"Non-Sporting","life_span":"12 - 15 years","temperament":"Playful, Loyal, Independent, Intelligent, Happy, Vocal"}],"id":"hYsMv3NQi","url":"https://cdn2.thedogapi.com/images/hYsMv3NQi.jpg","width":630,"height":423}]"
When we run fetch with a web address, it returns a Promise object. Promise is a basic Data Type in JavaScript. When you see Promise being returned, you can use the “await” operator in front of it to get to the actual return value.
When we await on fetch(“https://api.thedogapi.com/v1/images/search”), we end up receiving a Response object. On a moment’s glance, it looks like status is 200, and field ‘ok’ is true.
This is a good sign, it means our attempt to get JSON data from that web address successfully, but we don’t see the actual response yet.
Response object has a class member method called text(). When we call text(), we receive yet another Promise. No problem, let’s use another “await” on the whole thing. A string is returned containing the JSON Data we care about, this is good progress.
We can try to convert the JSON in string form into JSON objects (in this case an Array). This is usually done with the built-in JSON.parse() function. But, if you read the documentation on fetch, it turns out there is another convenience method called json(), which can be used like this:
> var data = await fetch('https://api.thedogapi.com/v1/images/search').then(result => result.json())
< undefined
> data[0].url
< "https://cdn2.thedogapi.com/images/x-LJYrq37.jpg"
First, we call fetch(‘https://api.thedogapi.com/v1/images/search') to get a Promise.
Next, we call .then() on the Promise object. This member function takes in a callback.
Callback takes this form:
inputVariable => (step1;step2;step3; return value)or(inputVariable) => {
step1;
step2;
step3;
...
return value;
}
Oh, that’s right, Callbacks are just functions without names! Also instead of the function keyword, we write =>
Promise and the then() function takes a callback, and when it is ready to give you the result, it will pass that as input into the callback.
Putting it altogether, we have this function:
function getDogImageUrl() {
const result = await fetch('https://api.thedogapi.com/v1/images/search').then(result => result.json())
return result[0].url;
}VM917:2 Uncaught SyntaxError: await is only valid in async function
Huh… we want to use await on the Promise, but Chrome tells us the await operator can only be used inside async functions.
Ok, let’s try this:
async function getDogImageUrl() {
const result = await fetch('https://api.thedogapi.com/v1/images/search').then(result => result.json())
return result[0].url;
}
Good going! Our function is ready for use. Next, we will cover how to make a single dog image show up on the screen.
Making Images
When the browser load any pages from the server, it converts that text page data into JavaScript objects and display its on the screen. If we google how to make images, we will quickly come across this page:
Which tells us how to make images:
var myImage = new Image(100, 200);
myImage.src = 'picture.jpg';
document.body.appendChild(myImage);
This snippet of code makes an image object with width = 100, and height = 200, sets its src as ‘picture.jpg’, and append it to the end of the document body.
If we try to run the same code, we get told that we don’t have that picture on our computer, so it failed. That’s ok though, we had some dog picture web addresses saved from earlier:
"https://cdn2.thedogapi.com/images/x-LJYrq37.jpg"
So we try:
var myImage = new Image(100, 100);
myImage.src = "https://cdn2.thedogapi.com/images/x-LJYrq37.jpg";
document.body.appendChild(myImage);
And that picture of dog has shown up here, you have scroll down to see them:
We can package this bit of code into a function:
function makeImage(url) {
var myImage = new Image(150, 150);
myImage.src = url;
return myImage;
}
Assembling both together
We have all the components we need. Let’s put it together:
async function makePhotoCollage() {
for (var i = 0; i < 50; ++i ) {
const url = await getDogImageUrl();
const image = makeImage(url);
document.body.insertBefore(image, document.body.firstChild);
}
}
First, we need to use the async keyword for this outer function due to calling getDogImageUrl() inside.
Second, instead of putting dog pictures at the end of the document, we want them to show up at the very top. So, we use document.body’s insertBefore method, and put our image object before the very first child.
Ready for dogs? call makePhotoCollage()
But wait, what about making them say “Woof!” when we click on them?
It turns out there is a standard function for adding that behavior too. Here are the modifications:
function makeImage(url, onClick) {
var myImage = new Image(150, 150);
myImage.src = url;
myImage.addEventListener('click', onClick);
return myImage;
}
async function makePhotoCollage() {
for (var i = 0; i < 50; ++i ) {
const url = await getDogImageUrl();
const image = makeImage(url, () => { alert('Woof!'); } );
document.body.insertBefore(image, document.body.firstChild);
}
}
In makeImage(), we take in a second input variable onClick.
onClick is a function/callback.
We use the built-in addEventListener() function on the image, which has two inputs:
- Event Type = ‘click’
- Event Handler = onClick
In makePhotoCollage(), we create a callback that sends an alert with the ‘Woof!’ string, and send it into the makeImage function.
Re-run makePhotoCollage() again, and try clicking on one of your dogs.
That’s all it took!
The Big Idea Behind these concepts
So, what’s the big deal?
Callbacks is used frequently in most JavaScript projects. What makes them interesting is that they can be passed around and used some time later.
In other programming languages, to achieve the same behavior, they would create a class with a single method call(), and pass instances of the class around. This means more work to do when you are only interested in passing in short steps to run.
On most modern websites, once the page is loaded, the browser will continue to retrieve dynamic data and images as user move around and click on the page. Guess what function they call to request/send data to the server? Fetch is the most likely candidate.
The JSON data format is the most commonly used format to pass data from and into web servers. When we need to interact with our own servers in future lessons, we will be using JSON for sure.
We are just a couple of lessons away from interacting with the entire internet and acing that interview!
In the next article: we will cover running JavaScript outside of the browser/Making calls to our own server!
Thanks for reading.