lec-js-08
I reckoned people were going to be crazy drained by the end of the semester, so I'm trying to grab folks while they're still only half zombies: March 18 - March 20.
JP incommunicado
April 5th: Project Submission
April 11th: Final Exam
It's our go-to weapon of choice when we want to consume an API endpoint.
🤔 What are some other examples of things that look like they're part of the JS language, but are actually something the web browser provides?
api/airport.php?code={some airport code}
api/slowairport.php?code={some airport code}
api/superslowairport.php?code={some airport code}They each respond with JSON like this:
{
"city": "Calgary",
"code": "YYC"
}The only difference in APIs is (as you might guess) how long it takes for the API to respond!
fast
slow
superslow
exp-01
let fetchResult = fetch(Endpoint.SUPERSLOW);
console.log(fetchResult);fetch() is typically really easy to use - you just feed it a string with the URI you're trying to hit.
We're getting some kind of object back.
And it seems to be...pending?
From the MDN docs on fetch()
fetch() can take in a second argument, options, that allows a lot of tweaking of fetch()'s behaviour.
You will need to pass in options to communicate with the TMDB API. See the docs (like these for Movie Details) for the individual endpoints for examples and useful code!
Those things in [[Double Brackets]]?
Although the console shows them, they are NOT ACCESSIBLE to the code we write!
This promise seems to be in a "pending" state...
...and has an undefined "result".
⚠️I'll often call [[PromiseResult]] just result, because it's easier to type on a slide.
So this Promise thingie changed significantly simply because we waited a while to look at it.
Huh.
Now the promise seems to be in a "fulfilled" state...
...and has a Response as a result.
Points 4 & 5 don't make any sense, do they???
What the heck is going on?!?
let fetchResult = fetch(Endpoint.SUPERSLOW);
console.log({ P });A Promise object { P } is immediately returned by fetch()...
...and behind the scenes, the web browser, spins up a minion to go make a request, grab the response, and...somehow change { P }.
{ P }
exp-02
Let's do our sneaky "attach things we want to see in the console to window" hack.
let fetchedPromise = fetch(Endpoint.FAST);
window.fetchedPromise = fetchedPromise;And let's also name our variable a bit more expressively to keep things very clear what we're dealing with.
I'm usually not a fan of variables like blahString, blahArray, etc. ... but with Promises, I'm willing to make an exception!
Hitting the . on this sucker shows that this Promise object doesn't have a ton of properties.
The vast majority of the time, you'll only ever use these 3.
And to be honest, the most frequently used one from those 3 is then().
=🥇
But what does then() actually do?
exp-03
If you look in the docs for then(), you'll see that it has two forms, and takes up to 2 callbacks as arguments.
The first form is the one most commonly used, so we'll focus our discussion on that.
🤔 That word "fulfilled" seems kinda familar...where did we see that before? We'll get back to that in a few slides.
function handleMovieSearch(clickEvent) {
let clickedElem = clickEvent.target;
// Do other awesome things.
}
someDomElem.addEventListener("click", handleMovieSearch);Remember how with the addEventListener() method, there was an "invisible" event argument being passed into our event handler callback?
event object passed, ninja-like, into handler
We'll provide an exploratory callback to see what arguments that callback is being passed.
let fetchedPromise = fetch(Endpoint.FAST);
function callbackExplorer(param1, param2, param3, param4) {
console.log("callbackExplorer called");
console.log("param1 is", param1);
console.log("param2 is", param2);
console.log("param3 is", param3);
console.log("param4 is", param4);
}
fetchedPromise.then(callbackExplorer);So it seems that the callback provided to then() is given 1 argument - that [[PromiseResult]] we're unable to access directly! And it seems that result is the API Response in the case of a fetch().
Yes, that Response is the same Response we saw earlier!
There's the API response.
With the payload of sweet JSON we want!
A Promise is fulfilled if the operation it was trying to do was completed successfully.
let fetchResult = fetch(Endpoint.SUPERSLOW);
console.log({ P });{ P }
When this happens, the Promise's internal state is changed from pending to fulfilled and the "armed" function is placed in a queue, with the result ready to be passed into it.
This slide is a placeholder for the on-the-board work done during the lecture.
A diagram involving web processes and a queue go here.
let fetchedPromise = fetch(Endpoint.FAST);
function callbackExplorer(param1, param2, param3, param4) {
console.log("callbackExplorer called");
console.log("param1 is", param1);
console.log("param2 is", param2);
console.log("param3 is", param3);
console.log("param4 is", param4);
}
fetchedPromise.then(callbackExplorer);(Technically, it can take 2 callbacks, but we're not going to worry about the 2nd one.)
exp-04
Maybe one of them can give us the JSON we want?
let fetchedPromiseResult;
let fetchedPromise = fetch(Endpoint.FAST);
function grabFetchedPromiseResult(resultPassedInByThen) {
fetchedPromiseResult = resultPassedInByThen;
}
fetchedPromise.then(grabFetchedPromiseResult);
window.fetchedPromiseResult = fetchedPromiseResult;
Maybe the debugger can help?!?
A
B
C
What order will these breakpoints be hit if we run this code?
Try and see!
If this were synchronous code - code that runs sequentially, with the browser waiting for each line to execute before proceeding - then B => A => C seems the sane answer.
A
B
C
But that's not what happens, is it?
We instead see B => C => A occur.
The code seems to be running asynchronously - literally,
"not synchronously".
A
B
C
😏
This slide is a placeholder for the on-the-board work done during the lecture.
import { Endpoint } from "./endpoint.js";
let fetchedPromiseResult;
let fetchedPromise = fetch(Endpoint.FAST);
function grabFetchedPromiseResult(resultPassedInByThen) {
fetchedPromiseResult = resultPassedInByThen;
}
fetchedPromise.then(grabFetchedPromiseResult);
window.fetchedPromiseResult = fetchedPromiseResult;exp-05
let fetchedPromiseResult;
let fetchedPromise = fetch(Endpoint.FAST);
function grabFetchedPromiseResult(resultPassedInByThen) {
fetchedPromiseResult = resultPassedInByThen;
window.fetchedPromiseResult = fetchedPromiseResult;
}
fetchedPromise.then(grabFetchedPromiseResult);Attach to the window inside our callback.
Success!
While that status property bears further lookin', let's park that.
What we're really interested in is that json, right? We're looking for the API endpoint's JSON, after all!
Is this some kind of a sick joke?
exp-06
let fetchedPromise = fetch(Endpoint.FAST);
function grabFetchedPromiseResultAndLogJson(resultPassedInByThen) {
let jsonCallPromise = resultPassedInByThen.json();
jsonCallPromise.then(log);
}
function log(whatever) {
console.log(whatever);
}
fetchedPromise.then(grabFetchedPromiseResultAndLogJson);import { Endpoint } from "./endpoint.js";
let fetchedPromise = fetch(Endpoint.SLOW);
function grabFetchedPromiseResultAndLogJson(resultPassedInByThen) {
let jsonCallPromise = resultPassedInByThen.json();
jsonCallPromise.then(log);
}
function log(whatever) {
console.log(whatever);
}
fetchedPromise.then(grabFetchedPromiseResultAndLogJson);Let's trace this from start to finish.
That's weird, right?
How would you expect THIS code to behave?
function loopAWholeBunch() {
for (let i = 0; i < 5_000_000_000; i += 1) {
// do absolutely nothing but spin wheels
}
}
let fetchResult = fetch(Endpoint.FAST);
loopAWholeBunch();
console.log(fetchResult);Should log out that Response thing, right?