JSON Warmup exercise:

The NASA API has an endpoint that allows us to retrieve Landsat 8 image for a specific date (date=2017-04-16) and location (lon=-122.3060, lat=47.6533) and link).

  • How do you find the date this image was taken?
  • What type is the cloud_score, and how would you access it?
  • How would you find the planet this was taken on?
  • How would you determine the service version?
let data = {
  "cloud_score": 0.3674638267064959,
  "date": "2017-04-16T19:01:16",
  "id": "LC8_L1T_TOA/LC80470272017106LGN00",
  "resource": {
    "dataset": "LC8_L1T_TOA",
    "planet": "earth"
  },
  "service_version": "v1",
  "url": "https://earthengine.googleapis.com/api/thumb?thumbid=a6793b04bb763f27283bd16f69f53ae6&token=d101cee0116e6f14891f3dbab8315dd0"
}

JSON

Link to resulting image. Where do you think this is?

Answers

let date = data.date;                 // date === "2017-04-16T19:01:16"
let cloud_score = data.cloud_score;   // cloud_score === 0.3674638267064959
let planet = data.resource.planet;    // planet === "earth"
let version = data.service_version;   // version = "v1"

JS

CSE 154

Lecture 13: AJAX, Fetch, and Promises

Agenda

Notes:

  • HW 3 will be out soon, but will have a LONG lead time (Midterm first!)
  • ETL Survey at the end of class

More on APIs

Testing with (hard coded) JSON

Intro to AJAX, fetch and promises

Another JSON Problem

All of the class' teams group names (with student names) were typed into a .json file which we will use in an example shortly.

The structure of the file is:

{
  "teams": [
    {
      "name": "Catz",
      "students": ["Jack", "Spot", "Whitney", "Charlie"]
    },
    {
      "name": "Dawgs",
      "students": ["Mowgli", "Ruby", "Moss"]
    }
  ]
}

JSON

What could would you write to tell how many students groups there are and how many students are actually in a group?

let data = {
"teams": [
  {
    "name": "Catz",
    "students": ["Jack", "Spot", "Whitney", "Charlie"]
  },
  ...
}
let numTeams = data.teams.length;  // numTeams === 44
let count = 0;
for (let i = 0; i < data.teams.length; i++) {
    count += data.teams[i].students.length;
}

JSON

How many people do you think actually signed up for a group? 113

P.S. Handy JSON Formatter Chrome extension for viewing JSON

Randomizer API

The Randomizer (now) uses the Randomizer API to get all of the team names and members.

The Randomizer Documentation can shed some light on how to get the data we need from this API

The CSE154 Randomizer

But how do we get that information into our web page????

Randomizer screen shot

(Click on the image and view page source on that page to see the example of the code...)

First attempt: hard code the data

Get the data from the server, add it to our .js file (loadTeamsTest), and then test our site with this "hard coded" text. We can ensure we are creating/removing our DOM elements correctly this way!

function loadTeamsTest() {
  data = {
    "teams": [
      {
        "name": "Catz",
        "students": ["Jack", "Spot", "Whitney", "Charlie"]
      },
      {
        "name": "Dawgs",
        "students": ["Mowgli", "Ruby", "Moss"]
      }
    ]
  };
  id("randomize").disabled = false;
}

JS

But what if the data changes frequently (i.e. we get Groupizer to work)?

AJAX

... to the rescue!!!

But first some background...

like why is it called AJAX???

AJAX the "old way" (XML over HTTP)

A way to use Javascript to pull in more content from the server without navigating the page to a new url.

We are showing you this for context only, DO NOT USE the "XML over HTTP" method of AJAX calls in this class. Read todays reading for why not.

let xhr = new XMLHttpRequest();
xhr.open(method, url, [async/sync]);
xhr.onload = function() { /* handle success */ };
xhr.onerror = function() { /* handle failure */ };
xhr.send(); 

JS (template)

let xhr = new XMLHttpRequest();
xhr.open("GET", "data.txt");
xhr.onload = function() { alert(this.responseText); };
xhr.onerror = function() { alert("ERROR!"); };
xhr.send();

JS (example)

AJAX
Asynchronous JavaScript and XML

The XMLHttpRequest object can be used synchronously or asynchronously.

So this functionality could be called S/AJAX or A/SJAX. But AJAX has a nice ring to it.

It's better to use async so that the page doesn't block waiting for the page to come back.

We will not use AJAX synchronously in this class.

AJAX
Asynchronous JavaScript and XML

the XMLHttpRequest object can be used to fetch anything that you can fetch with your browser.

This includes XML (like in the name), but also JSON, HTML, plain text, media files.

So it could be called AJAJ or AJAH or AJAT. But AJAX has a nice ring to it.

AJAX with the Fetch API

The Fetch API was created in 2014 and incorporated into the window DOM object.

We will be using AJAX with Fetch in this class.

(function() {
...
  function doWebRequest() {
    let url = ..... // put url string here
    fetch(url);
    // Note: there needs to be more here
    // process data and catch errors
  }
})();

JS (template)

Example: AJAX with the Fetch API

You can use the absolute path to a file (a full URL) as the parameter to fetch

const URL = "https://courses.cs.washington.edu/courses/cse154/19sp/tools/randomizer/randomizer.php";
...
function loadTeams() {
  fetch(url + "?mode=text");
  ...
}

JS (example)

You can also use a relative path name to fetch from a file that is retreived from the same directory on the server as the .js file.

const URL = "randomizer.php";
...
function loadTeams() {
  fetch(url + "?mode=text");
  ...
}

JS (example)

Why use AJAX?

  • You can use AJAX to download information from a server in the background
  • It allows dynamically updates to a page without making the user wait
  • It avoids the "click-wait....refresh" which would frustrate users
ajax diagram

Processing the returned data

Now that we've done a fetch, we need to do something with the data that comes back from the server.

But we don't know how long that will take or if it even will come back correctly!

The fetch call returns a Promise object which will help us with this uncertainty.

Promises

Real world promises

Promises have three states:

  • Pending
  • Fulfilled
  • Rejected

Example: “I promise to post HW 3”
Pending: Not yet posted
Fulfilled: HW 3 posted
Rejected: Wrong homework posted, or not posted in time

JavaScript Promises

promise
A JS object that executes some code that has an uncertain outcome

Promises have three states:

  • Pending
  • Fulfilled
  • Rejected
...
  let promise = new Promise(action);
...
function action(resolve, reject) {
  // do pending uncertain action
  //  (like make an AJAX call)

  if (success) {
    resolve();     // Fulfilled
  } else {
    reject();      // Rejected
  }
}

JS (template)

A Promise is returned from a fetch call

We will be using promises when we fetch information from a server, which is an uncertain task

We give you "boilerplate" starting code because you will use this frequently

AJAX fetch Code Skeleton (text response)

const BASE_URL = /* put base url string here */;
...
function callAjax() {
  let url = BASE_URL /* + any query parameters */;
  fetch(url)
  .then(checkStatus)
  .then(function(responseText) {
    //success: do something with the responseText
  })
  .catch(function(error) {
    //error: do something with error
  });
}
function checkStatus(response) {
  if (response.status >= 200 && response.status < 300) {
    return response.text();
  } else {
    return Promise.reject(new Error(response.status + ": " + response.statusText));
  }
}

JS (template)

Found from ajax-template-documented.js

Better AJAX fetch Code Skeleton (text)

const BASE_URL = /* put base url string here */;
...
function callAjax() {
  let url = BASE_URL /* + any query parameters */;
  fetch(url)
  .then(checkStatus)
  .then(handeResponse)
  .catch(handleErrror);
}
function handleResponse(responseText) {
  //success: do something with the responseText
}

function handleError(error) {
  //error: do something with error
}

function checkStatus(response) {    // boiler plate code given out
  ...
}

JS (template)

Ajax fetch Code Skeleton (with JSON)

What if the resulting text comes back as JSON format?

const BASE_URL = /* put base url string here */;
...
function callAjax() {
  let url = BASE_URL /* + any query parameters */;
  fetch(url)
  .then(checkStatus)
  .then(JSON.parse)      // parse the response string into a JSON object
  .then(handeResponse)
  .catch(handleErrror);
}
function handleResponse(responseJSON) {
  // now handle this response as a JSON object.
}

function handleError(error) {
  // error handling doesn't change
}

JS (template)

Mechanics

We initiate a fetch of a URL

  • A fetch call returns a Promise object
  • The .then method on a Promise object returns a Promise object
    • Our first .then(checkStatus) checks the status of the response to makes sure the server responded with an OK. The result of that first .then is another Promise object with the response (text, JSON, ...) as the value of the Promise.
    • We may .then(JSON.parse) which also returns a Promise object with a JSON object as the value
    • We .then(handleResponse) which will do something with the response from the server.
    • If at any time there is an error, the execution falls down to the .catch method on the Promise chain

Chaining of Promises gives us a nice data flow, like down a pipe!

The Promise Pipeline

Visual description of the fetch pipeline

Promises are good because...

They help deal with code that has an uncertain outcome

They separate the completion of the fetch request from the page logic

  • We can reuse the same logic and handle completion in different ways (e.g. refactor the AJAX logic or the function to handle the response)

Back to our randomizer example

function loadTeamsTxt() {
  const url = URL + "?mode=text";
  fetch(url)
    .then(checkStatus)
    .then(handleLoadTeams)
    .catch(console.log);
}

function loadTeamsJSON() {
  const url = URL + "?mode=json";
  fetch(url)
    .then(checkStatus)
    .then(JSON.parse)
    .then(handleLoadTeams)
    .catch(console.log);
}
              

JS

Summary: Why are promises/fetch useful?

The help deal with uncertainty in your code. You never know exactly what will happen when you make an AJAX call, so wrapping the call in a Promise is a nice way to deal with the uncertainty.

The paradigm is nice because you write the anonymous function that defines the promise, so you are the one who writes the code that determines whether the promise was 'fulfilled' or 'rejected'.

You also define what happens after the Promise fulfills with the then function, and what happens when it rejects in the catch function.

AJAX Summary

  • AJAX is not a programming language; its a particular way of using JavaScript
  • It uses JS to download information from the server in the background
  • It allows dynamically updating a page without making the user wait
  • Using the Fetch API for AJAX can produce "cleaner" and more maintainable code
  • JSON is now more common than XML for processing formatted data, but both are just ways to store data
  • Examples of AJAX fetch in use: UW's CSE 14x Diff Tool,  Practice-It; Amazon product pages, most auto-complete search features, Randomizer, APOD examples.
ajax diagram