Lecture 12

APIs, Promises, and Fetch

Administrivia

Midterm next Monday, 5:15 KNE 110

You must let me know by Friday if you have a conflict.

HW2 due Wednesday

You must click Turn-In to receive credit

Homework 2 Turn in button

Agenda

Asynchronous JS & APIs

Promises?

Fetching from APIs

Poll Everywhere

Fitz's poll everywhere

Callback to callbacks

Recall our callbacks? Way to pass a function as a parameter and get a response later.

Saw this with timeouts and intervals:

  • Timeout: I know that in exactly (more or less) 20 minutes I have to take the cookies out of the oven
  • Interval: I know that exactly (more or less) every 5 minutes, I want to switch between running and walking (and then maybe clear that after 30 minutes).

But what if we don't know exactly?

Poll

Callback to HTTP, TCP, IP

All of these providing the browser with a way to get your website from a server.

Following strict protocols:

GET /index.html HTTP/1.1
Host: www.example.com

Every request follows the same pattern:

METHOD PATH VERSION

Very detailed protocol

These protocols allow two applications, the browser, and the webserver, to have a well-defined interface to talk to each other with.

Programmers of these applications know they must follow this spec to access the internet

API

Application Programming Interface

APIs come in all shapes and sizes:

  • REST
  • SOAP
  • RPC/gRPC
  • JSON over HTTP

Most of what we'll see is based on or built on top of HTTP.

Link

API Pattern:

Client wants something

Server has something

Poll

Client and Server

Analogy: You're out to get some pizza, so you:

  • Request menu
  • Order pizza
  • Check pizza
  • Eat pizza
  • Pay for pizza

At each step, can’t continue before the previous finishes. We as the client (pizza eaters) need the server (pizza provider) to do something on our behalf.

Callback (again) to Callbacks:

We can imagine all of these steps as a series of callbacks, depending on the event previous to them:

function order() {
  setTimeout(function() {
    makeRequest("Requesting menu...");
    setTimeout(function() {
      makeRequest("Ordering pizza...");
      setTimeout(function() {
        makeRequest("Checking pizza...");
        setTimeout(function() {
          makeRequest("Eating pizza...");
          setTimeout(function() {
            makeRequest("Paying for pizza...");
            setTimeout(function() {
              let response = makeRequest("Done! Heading home.");
              console.log(response);
            });
          });
        });
      });
    });
  });
}

JS

Server Requests

And that makeRequest function? Still more callbacks:

makeRequestToServer(firstUrl, dataToServer, function(response1) {
    // function to call when server is done responding
    let jsonData = JSON.parse(response1);
    processResponse1(jsonData, function(processedData1) {
        getUserInput(processedData1, function(userResponse) {
            makeRequestToServer(secondUrl, userResponse, function(response2) {
                processResponse2(response2, function(processedData2) {
                    // Do something with processedData2.
                });
            });
        });
    });
}

JS (psuedocode)

Code is very confusing and hard to follow the logical flow of execution and data

What if we could attach callback functions for each step like a pipeline to be easier to follow?

Poll

Fetching Data Asynchronously

This is exactly our "Asynchronous" JavaScript. But we have to be careful (the internet is an... unusual place).

  • What kind of data can we get from the internet?
  • How do we request data from the internet?
  • What happens when something goes wrong on the internet?
  • Can you trust everything you find on the internet?

Asynchronous Programs

asynchronous request diagram

Timer callback functions are called after a fixed delayMs and don't have uncertain behavior

AJAX callback functions are not guaranteed to execute after a fixed timespan, and their responses are uncertain (resolved, rejected, "misplaced".)

AJAX: Asynchronous JavaScript and XML

Not so much "XML" anymore, but everything else stands and we still use the term.

AJAX Everywhere

It's become a fundamental part of the web: pages are dynamic, loading different content depending on what the user does, who the user is, what time of day it is, or how the internet is feeling that day.

With callbacks we ended up with callbacks calling callbacks calling back to a previously given callback. This “callback hell” is extremely challenging to debug:

  • When did this happen?
  • Where did it come from?
  • Where did it go?
  • Did it happen when I wanted it to?
  • Why did it fail?
  • Why did it never ever happen at all?

And so, we came up with another plan...

Poll

Promises

Three states of a Promise: pending, resolved, rejected

Promises are a sort of contract:

  • Something will happen
  • You can have multiple things happen.
  • And catch any errors.

Can only go from Pending to Fulfilled or Rejected (no takebacks)

Example: "I promise to return to your table"

  • Pending: Waiting for my pizza
  • Fulfilled: Pizza has arrived!!
  • Rejected: Kitchen ran out of cheese. :(

Promises on MDN

Chaining Promises

Promises allow us to chain things together:

orderPizza()
  .then(inspect)
  .then(eat)
  .then(pay)
  .catch(badPizza);

Promises + API calls

When calling an API, we have no idea when it’ll return.

  • Where’s the server located?
  • Where is it located in relation to us?
  • Are there any traffic jams on the internet?
  • Is the server running slow today?
  • Did the server change something about what it expects in requests?

AJAX with Fetch

(function(){
        ...
function doWebRequest() {
  const url = ..... // put url string here
  fetch(url); // returns a Promise!
}

JS (template)

AJAX with Fetch

fetch was created in 2014 and incorporated into the global window.

fetch takes a URL string as a parameter to request data (e.g. menu category JSON) that we can work with in our JS file.

function populateMenu() {
  const URL = "https://cse154.appspot.com/cafeexample/";
  fetch(URL + "/getCategories") // returns a Promise!
    .then(...)
    .then(...)
    .catch(...);
 }

JS (example)

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.

Using Promises with fetch

We won't be constructing Promises, but will be using them when we fetch information from a server, which is an uncertain task

The returned Promise contains a value that is a Response from the requested resource, which we can get then. after the fetch call.

fetch(url).then((resp) => {
  // resp is a Response object!
});

JS

But what's in a Response?

Most important first step:

Did the fetch return a response ok?

200 OK

The Response object

Property Description
response.status Status code (e.g. 200, 400, etc.)
response.ok Whether the response has a success (2XX) status code, short for:
response.status > 200 && response.status <= 300)
response.statusText Status text returned in the response (e.g. "200 OK", "400 Bad Request", "503 Service Unavailable")
response.json(), response.text() Methods to extract the response body depending on the data format

We will use the Response returned (in a Promise) from fetch to:

  1. Check response.ok
  2. If not, throw an Error which will jump immediately to a catch statement. It's good to construct the Error with details about the response.statusText
  3. Otherwise, extract the data we want with response.text() or response.json()

AJAX fetch Code Skeleton: V1

function fetchTheData() {
  let url = URL + possibleParameters;
  fetch(url)
    .then(function(response) {
      if (!response.ok) { // response.status < 200 || response.status >= 300
        throw Error("Error in request: " + response.statusText);
      }
      return response; // only reach here if no Error thrown
    })
    .then(function(response) {
       return response.json();   // for JSON responses
       // return response.text(); // for text responses
    })
    .then(function(data) {
       // success: do something with the data (JSON or text)
    })
    .catch(function(err) {
       // error: handle request error (e.g. display message
       // on the page)
    });
}

JS (template)

AJAX fetch Code Skeleton (better version)

The following is much easier to read, and factors out larger callback functions into named functions. Remember that the return of each callback function passed to a then is exactly the argument passed into the callback of the next then.

For short callbacks, we also see the motivation with arrow function syntax!

function fetchTheData() {
  fetch(url + possibleParameters)
    .then(checkStatus)
    .then(response => response.json())    // if json
    // .then(response => response.text()) // if text
    .then(processResponse)
    .catch(handleError);
  }
}

function processResponse(data) { // success: do something with the response data }

function handleError(err) { // e.g. display helpful error message on the page }

// You should use this always after fetch and before processing the data
function checkStatus(response) {
  if (!response.ok) {
    throw Error("Error in request: " + response.statusText);
  }
  return response; // a Response object
}

JS (template)

Looking Ahead

Make sure to focus on studying for the midterm, but Wednesday we'll be talking about a particular response type: JSON. Then we'll be practicing making API calls to various APIs.