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
Asynchronous JS & APIs
Promises?
Fetching from APIs
Recall our callbacks? Way to pass a function as a parameter and get a response later.
Saw this with timeouts and intervals:
But what if we don't know exactly?
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
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
Application Programming Interface
APIs come in all shapes and sizes:
Most of what we'll see is based on or built on top of HTTP.
Client wants something
Server has something
Analogy: You're out to get some pizza, so you:
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.
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
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?
This is exactly our "Asynchronous" JavaScript. But we have to be careful (the internet is an... unusual place).
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.
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:
And so, we came up with another plan...
Promises are a sort of contract:
Can only go from Pending to Fulfilled or Rejected (no takebacks)
Example: "I promise to return to your table"
Promises allow us to chain things together:
orderPizza()
.then(inspect)
.then(eat)
.then(pay)
.catch(badPizza);
When calling an API, we have no idea when it’ll return.
(function(){
...
function doWebRequest() {
const url = ..... // put url string here
fetch(url); // returns a Promise!
}
JS (template)
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.
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
?
Did the fetch return a response ok?
Response
objectProperty | 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:
response.ok
catch
statement. It's good
to construct the Error with details about the response.statusText
response.text()
or response.json()
fetch
Code Skeleton: V1function 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)
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)
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.