Start Here: Zero to JavaScript — Callback/Fetching data/Working with JSON

Jack Yeh
TeamZeroLabs
Published in
8 min readNov 21, 2020

Bite size Javascript Lesson you can finish in 3–5 minutes!
Missed the previous lesson on Built-in functions and Class? Go here.

Photo by Alvan Nee on Unsplash

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!

End result: 100% happiness.

So, before we can show all of these amazing dogs, we need to figure out:

  1. Where do I get images of dogs?
  2. How do I make a single dog picture show up in the tab, and say “Woof!” when clicked?
  3. 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:

Making progress!

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

Easy! Now I will never run out of dog pictures to look at.

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:

  1. Event Type = ‘click’
  2. 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.

Easy as PIE

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.

--

--

Jack Yeh
TeamZeroLabs

I monitor your full stack deployment in production, so you can sleep at night. Docker | Kubernetes | AWS | Prometheus | Grafana