Summer Homework #4 đ» đ„
New stuff we learned this week: đ€
While Loop
- the
while
loop is a very simple loop that continues to execute as long as the condition is true:
let x = 0;
while (x < 5) {
console.log(x);
x++;
}
- a normal
for
loop just sort of pulls up the initialization and increment steps of awhile
loop into one line, so itâs usually preferred. But sometimes there are things that can only be expressed with a while loop, so itâs pretty handy. - one interesting case for using a
while
loop is to create an infinite loop (this needs to be done carefully, with some mechanism for breaking out of the loop):
while (true) {
// wheee!!! infinite loop
}
- the
break
keyword allows you to break out of a loop (works forwhile
loops andfor
loops):
let x = 0;
while (true) {
console.log(x);
x++;
if (x > 100) {
break; // BREAK out of `while` loop
}
}
Case Statement
- the
case
keyword in javascript (and mostc
-like languages) allows you to make a multi-way logical decision. Itâs basically a more readable form of a longif / else if
chain, for when the number of options gets bigger than 3 or so:
function greetMom(htcStudent: string): void {
switch (htcStudent) {
case "Tabitha":
console.log("Greetings, Stacey!");
break;
case "Win":
console.log("Greetings, Rachel!");
break;
case "Harriet":
console.log("Greetings, Rachel!");
break;
case "Willow":
console.log("Greetings, Jessie!");
break;
case "Kiah":
console.log("Greetings, Jessie!");
break;
default:
console.log("Greetings, unknown mother!");
break;
}
}
- the
break
keyword causes javascript to exit the switch statement. If you leave it out, the condition falls through into the next case. This is usually a bug, but sometimes it can be desired. We could rewrite the above function to be shorter, taking advantage of fall-through for the siblings, like so:
function greetMom(htcStudent: string): void {
switch (htcStudent) {
case "Tabitha":
console.log("Greetings, Stacey!");
break;
case "Win":
case "Harriet" /* fall through đȘ */:
console.log("Greetings, Rachel!");
break;
case "Willow":
case "Kiah" /* fall through đȘ */:
console.log("Greetings, Jessie!");
break;
default:
console.log("Greetings, unknown mother!");
break;
}
}
- the
default
keyword handles things if none of the cases match. It is not required, but is often very useful. Itâs like the lastelse
of anif / else if / else
set. - because the
return
keyword always causes the function to immediately exit, thereturn
keyword can be substituted forbreak
when it makes sense. Imagine we wanted to re-write the above function to just return the motherâs name, we could usereturn
instead ofbreak
to make it more succinct:
function getMom(htcStudent: string): string {
switch (htcStudent) {
case "Tabitha":
return "Stacey";
case "Win":
case "Harriet" /* fall through đȘ */:
return "Rachel";
case "Willow":
case "Kiah" /* fall through đȘ */:
return "Jessie";
default:
return "Unknown mother";
}
}
Generators & Iterators đȘ
- generators are super-powered functions that return iterators, they have magic powers of being pausable and resumable.
- you declare a generator using the special
*
symbol after thefunction
keyword.
function* myGenerator() {
// yield stuff here
}
- a generator function is not immediately called when invoked, unlike normal functions. Instead, it returns an iterator â which is a special object with a
.next()
method: - when you call the
.next()
method of an iterator, you get an object that has two properties:.value
and.done
. The typescript type would be:
type ReturnedByDotNext = {
value: any;
done: boolean;
};
- iterators yield values, using the special new
yield
keyword. Whatever they yield becomes the.value
property of the object returned by the iteratorâs.next()
method. When there is nothing left to yield,iterator.next()
returns{ value: undefined, done: true }
:
function* myGenerator() {
yield "goatbanjorodeo";
}
const myIterator = myGenerator();
// `one` is: { value: 'goatbanjorodea', done: false }
const one = myIterator.next();
// `two` is: { value: undefined, done: true }
const two = myIterator.next();
- iterators (created by generators), usually yield more than one value, so you can sort of think of them as a fancy way to create an array:
// sort of like a fn that returns [0, 1, 1, 2, 3, 5]
function* fibGenerator() {
yield 0;
yield 1;
yield 1;
yield 2;
yield 3;
yield 5;
}
- there is a built-in language construct for looping over an iterator, the
for...of
loop:
function* goatbanjorodeoGenerator() {
yield "goat";
yield "banjo";
yield "rodeo";
}
// đ cool! new syntax
for (let word of goatbanjorodeoGenerator()) {
console.log(word); // `goat` `banjo` `rodeo`
}
- you can make instances of any class into an iterable by adding the special method
[Symbol.iterator]
:
class Life {
constructor() {
this.kingdoms = ["plant", "animal", "fungus"];
}
// WHOA, this is the weird part
// a method whose name is a SYMBOL đ§
[Symbol.iterator]() {
let index = 0;
// but here we just return an OBJECT with
// a `.next()` function making our own iteratable
return {
next: () => {
if (index < this.kingdoms.length) {
const value = this.kingdoms[index];
index++;
return { value: value, done: false };
} else {
return { value: undeifned, done: true };
}
},
};
}
}
const life = new Life();
// đ we have our dream, we can `for...of` over our object
for (let kingdom of life) {
console.log(life); // `animal` `plant` `fungus`
}
- but the cool thing is lots of useful things already are iterables because under the hood, they implement the
[Symbol.iterator]
method. This includes arrays and strings:
let myArray = ["goat", "banjo", "rodeo"];
// đ€ way more ergonomic than a normal `for` loop
for (let word of myArray) {
console.log(word); // `goat` `banjo` `rodeo
}
for (let letter of "goat") {
console.log(letter); // `g` `o` `a` `t`
}
Symbols in JS
- symbols were created so that the javascript language could evolve, adding methods with names guaranteed not to conflict with any userland code.
- you can make your own symbols like this:
const foo = new Symbol("foo");
- symbols are automagically unique, so two symbols constructed with the same string are still different:
new Symbol("foo") === new Symbol("foo"); // FALSE
- there are some built-in Symbols added by javascript itself, like
Symbol.iterator
andSymbol.replace
. These allow both the language itself, and your code, to implement new language features, like in theclass Life
example above.
Async / Await
- one advanced feature of generators is that you can pass arguments BACK into the function. Consider this snippet of code:
function* myGenerator() {
yield "goat";
let last = yield "banjo";
yield last;
}
const iterator = myGenerator();
console.log(iterator.next());
console.log(iterator.next());
console.log(iterator.next("rodeo"));
- step very carefully through the execution with me:
- on line 8, the first time we call
iterator.next()
the function yields out"goat"
, so we log:{value: 'goat', done: false}
- on line 9, the first time we call
iterator.next()
the function yields out"banjo"
, so we log:{value: 'banjo', done: false}
- but then on line 10 we pass a value BACK into the generator. This value (the string
"rodeo"
) gets substituted for the lastyield
expression, making it as if line 3 read:let last = "rodeo";
- so, while executing line 10
last
is set to"rodeo"
and the final call toiterator.next()
logs out{value: 'rodeo', done: false}
; - NOTE: this does work, my example in class failed because I was off-by-one â I was passing the value to the wrong invocation of
iterator.next()
. Sorry about that! đŹ
- on line 8, the first time we call
- once javascript had generators with their magical powers of being RESUMABLE functions and the power to pass values back in at a later time, someone had the bright idea to use them to make async code LOOK synchronous. What follows is an oversimplified version of how this might work:
function* syncLookingJsonFetcher() {
// look at this line very carefully.
// `fetch()` returns a PROMISE,
// so we are YIELD-ing a promise to the outside world
// but then we are setting the `response` variable
// to whatever the outside world passes back
// in the second time they call `.next()`
const response = yield fetch("http://api.mysite.io");
// now imagine `response` is the actual HTTP response
// (how we do that comes below)
// we can now do the same trick to get the JSON,
// we YIELD out another PROMISE
const json = yield response.json();
// now we have our dream, we could use `json`
// and all the code above LOOKS synchronous
}
// NOTICE we're OUT of the generator function here
// this is the "OUTSIDE WORLD"
// our sweet function needs help from the outside world
// this is ugly, but it will sort of work:
const iterator = syncLookingJsonFetcher();
// start by getting hold of the promise yielded on line 8
const fetchPromise = iterator.next().value;
fetchPromise
.then(response => {
// when that promise resolves,
// PASS it back INTO the generator
iterator.next(response);
// now we need to call `.next()` to get
// the `.json()` promise out from line 14
const jsonPromise = iterator.next().value;
// return the promise so we can CHAIN
return jsonPromise;
})
.then(json => {
// now pass the resolved JSON back INTO the generator
// and it becomes the value of `json` on line 14
iterator.next(json);
});
- carefully read that whole snippet of code through, line by line. I think itâs the hairiest chunk of javascript Iâll ever give you. Read it a couple times, see if you can get to the point where you have a decent idea of whatâs actually happening.
- now that youâve seen that awful code, you can be thankful that Javascript added a bunch of syntatic sugar into the language to make doing that super easy, and they basically wrote the whole âoutside worldâ section for you. The end result is async / await:
// here's that same horrible code from above
// rewritten with async / await
// NOTICE the `async` keyword before `function`
// this is like the `*` in a generator function
// it's a clue to Javascript that this is a special
// generator-like function under the hood
async function doApiThings() {
// here notice that instead of `yield` we `await`:
const response = await fetch("http://api.mysite.io");
// then we can await on the second promise too:
const json = await response.json();
// now we can do whatever we want with the json!
insertStuffInDom(json);
}
// of course you do need to call your async function :)
doApiThings();
- hereâs that again without all the comments:
async function doApiThings() {
const response = await fetch("http://api.mysite.io");
const json = await response.json();
insertStuffInDom(json);
}
doApiThings();
- finally, we donât have a
.catch()
or an error argument (like in nodeback-style), so we usetry / catch
to handle errors with async / await:
async function doApiThings() {
try {
const response = await fetch("http://api.mysite.io");
const json = await response.json();
insertStuffInDom(json);
} catch (error) {
// do error handling things here
alert("Oh noes!");
}
}
doApiThings();
- note: the
await
keyword can ONLY be used (currently) inside anasync
function. You may get an error if you try to useawait
outside of anasync
function. In 6-18 months this wonât be true anymore (javascript is adding whatâs called âTop-level awaitâ), but support is limited now.
Useful Links:
Homework Plan (next class in 3 weeks)
- 1 day (per week) review all flashcards
- 1 day (per week) touch typing practice
- 1 day Case statement homework
- 1 day Generators homework
- 1 day Async/Await homework
- 2 days vim review homework
- 1 day arrow functions review homework
Homework (week 1)
Homework (week 2)
Homework (week 3)
Generators Homework
- slowly and carefully review the âGeneratorsâ section of âNew Stuffâ ^
- go to this url and create a FORK of the repo
- connect to the HTC machine with vscode and clone the repo into a folder called
~/node/summer-4/
- create a new branch to work on
- Type a command to install the dependencies.
- check the
package.json
to see the two scripts I made for you, these will come in handy. - open up the
src/generators.spec.ts
file and thesrc/generators.ts
files - one by one, changing each
xit()
to ait()
, make all the tests pass. Youâll need to create functions ingenerators.ts
and export/import them as needed. - make sure to have good, strict Typescript types for arguments (but you can skip adding return types for the generators, thatâs too complex for now).
- commit your work.
- push up a Merge Request to Gitlab, and post it in Slack.
Case Statement Homework
- slowly and carefully review the âCase Statementâ section of âNew Stuffâ ^
- go to this url and create a FORK of the repo
- connect to the HTC machine with vscode and clone the repo into a folder called
~/node/summer-4/
- create a new branch to work on
- Type a command to install the dependencies.
- check the
package.json
to see the two scripts I made for you, these will come in handy. - open up the
src/case.spec.ts
file and thesrc/case.ts
files - one by one, changing each
xit()
to ait()
, make all the tests pass. Youâll need to create functions incase.ts
and export/import them as needed. - make sure to have good, strict Typescript types for arguments AND function returns.
- commit your work.
- push up a Merge Request to Gitlab, and post it in Slack.
Async/Await Homework
- slowly, and repetitively, and very carefully read the âAsync/Awaitâ portion of âNew Stuffâ above. Try to get a working understanding of HOW async/await works by building on top of generators.
- then, clear your mind of all of the complicated generator theory stuff, and review the last two code snippets in the âNew Stuffâ â those really simple code snippets are very similar to all you will need to do this homework.
- just to be clear: for this homework you will NOT create ANY generator functions, you will ONLY use
async
functions. - connect in vscode to your
~/www/summer-3
web/promises git repo. - keep all of the functionality the same, but rewrite it with NO promises, rather, using
async / await
. That means donât change anything about the HTML or CSS, you should be able to re-use all that code. The only thing youâre going to be changing is that instead of using promises directly and calling.then()
on them, youâre going to do your async fetching and unpacking of JSON usingasync / await
. - hint: youâll need some sort of main
async
function, inside it you canawait
on calls tofetch()
andawait
on turning http responses intojson
just like the examples in the New Stuff above. - make sure to handle errors using
try / catch
. - even if your browser supports âtop-level awaitâ, I want you to wrap all of your usages of
await
inside anasync
function. - commit your work, and slack me the GitLab url when you are done
Vim Homework (Review) #1
- pull out your flashcards, and separate out all of the
vim
ones - go through each flashcard and for every command that you donât use all the time take around 90 seconds to practice it with your fingers (not just review it mentally).
- after your flashcard review, ssh into the htc machine and do all of
vimtutor
, with the following changes:- skip lesson 6.5 to the end (thatâs 6.5 and all of lesson 7)
- do the task each step wants you to do, but you donât have to do it the way they tell you, if you know a better way, use the faster, better way
- slowly and carefully review the
vim
section of the week 28 homework - ssh into to the HTC machine (donât use VSCODE for this), and clone this repo into
~/summer-4
:git@gitlab.howtocomputer.link:htc/vimtutor.git
- then cd into the
vimtutor
dir that was created bygit clone
and open thevisual-block-1.txt
file withvim
. - work through all of the steps of the lesson
Vim Homework (Review) #2
- make sure that at least 1 full day has passed since you did Vim Homework #1 â your learning wonât be near as effective without spacing out the review
- ssh into the HTC machine again (no VSCODE) and cd into
~/summer-4/vimtutor
then dovim visual-block-2.txt
and complete all of the steps. - the steps are the same as the other visual-block lesson, but there are fewer hints given to you.
Arrow Functions (Review) Homework
- slowly and carefully review the Javascript portion from week 30.
- make a fork of this repo and then clone down your fork using VSCODE, into a
~/summer-4
dir. - make a new branch
- edit the
arrow-fns.js
file, adding all of the âanswersâ where indicated - push up your branch and make a MR, slack me the MR url, I might leave some comments for you to change things