Daily Learning Notes for June 19th, 2017

Today I had a great JavaScript study session with my friend, I assembled a robot for the first time while hosting a beginner’s robotics workshop for our meetup group with my friends from Acrobotic, and then I had a great rock climbing session with another friend and then stayed up late doing math problems and hanging out with my coworking friends!

It’s ES2015 (ES6) study time! My friend suggested we pick some random topics from the ES6 In Depth articles from Mozilla Hacks.

Examples of the let statement

Like they say, let is the new var! It’s finally time for me to join the cool kids and see what the hype is all about! I already knew that you can use let to define variables with block scope, not just function scope, but I didn’t know just how powerful it is for working with loops! My friend showed me a great example.

First, here’s some broken code:

for (var i = 0; i < 3; i++) {
      setTimeout(function () {
         console.log("i is: " + i);
      }, i * 1000);
}

Instead of printing each value of i from 0 to 2 as expected, here setTimeout’s callback function runs after the loop has already finished, so that i has a value of 3 for every message! It’s a tricky situation, running an asynchronous function within a synchronous loop.

So this is more or less what’s happening (as I understand it so far):

var i = 0;

// Loop iteration 1
	// setTimout starts, callback function #1 is queued up!
	i = 1;

// Loop iteration 2
	// setTimout starts, callback function #2 is queued up!
	i = 2;

// Loop iteration 3
	// setTimout starts, callback function #3 is queued up!
	i = 3;

// End the loop!

// After a delay, the callbacks run and access the same variable i, which is 3!
	// Callback #1
	console.log("i is: " + 3);

	// Callback #2
	console.log("i is: " + 3);

	// Callback #3
	console.log("i is: " + 3);

To fix this using only the older features of JavaScript, you could create a little work-around that takes advantage of function scoping!

Old solution using function scoping with an IIFE:

for (var i = 0; i < 3; i++) {
	(function(i) {
	  setTimeout(function () {
	     console.log("i is: " + i);
	  }, i * 1000);
	})(i);
}

Here we use an Immediately-Invoked Function Expression (IIFE) to create a local scope that essentially freezes each value of i before the callback function has a chance to run. It’s like creating three little alternate universes, each with its own version of i saved as a local variable.

So here’s what’s happening in the example above (as I understand it so far):

var i = 0;

// Loop iteration 1
	// Callback function #1 is queued up, with i = 0!
	(function(i) {
	  setTimeout(function () {
	     console.log("i is: " + i);
	  }, i * 1000);
	})(0);

	i = 1;

// Loop iteration 2
	// Callback function #2 is queued up, with i = 1!
	(function(i) {
	  setTimeout(function () {
	     console.log("i is: " + i);
	  }, i * 1000);
	})(1);

	i = 2;

// Loop iteration 3
	// Callback function #1 is queued up, with i = 2!
	(function(i) {
	  setTimeout(function () {
	     console.log("i is: " + i);
	  }, i * 1000);
	})(2);
	
	i = 3;

// End the loop!

// After a delay, the callbacks run and access their own personal copies of the variable i:
	// Callback #1
	console.log("i is: " + 0);

	// Callback #2
	console.log("i is: " + 1);

	// Callback #3
	console.log("i is: " + 2);

Here’s another fix, which also makes use of function scope and works exactly the same way, except here we’re defining a named function instead of an anonymous IIFE. But the solution works the same way as before! We’re creating three function calls, each with their own version of i encapsulated within their local scope:

function write(i) {
  setTimeout(function () {
    console.log("i is: " + i);
  }, i * 1000);
}

for (var i = 0; i < 3; i++) {
   write(i);
}

My friend and I were talking about how some of the new JavaScript features feel like solutions to problems we don’t really encounter, because we’ve just grown accustomed to solutions like the code above. But other languages don’t suffer from this problem, and there is a better way! Let’s fix it the ES6 way – let to the rescue!

for (let i = 0; i < 3; i++) {
      setTimeout(function () {
         console.log("i is: " + i);
      }, i * 1000);
}

Because let creates a block-level scope for each iteration of the loop, now it’s as if we’re creating little alternate universes just like before, but we don’t need to write any extra functions to do so! This example of the power of let feels downright magical to me, and I was in a good mood the rest of the day after this little mind-blown moment!

If you want to see additional proof of how this works, you can just copy-paste the above code into the online Babel REPL and see how it transpiles into pre-ES2015 JavaScript. As you can see here, Babel converts the version using let into what looks just like the previous solution, using another function to isolate each version of i within its local scope!

Other notes and stuff to look at later:

  • To read later: “You Don’t Know JS” chapter on function and block scope

  • If used to define a global variable, let does not add it as a property of the window object like var does! (The “global let scope”.)

  • You can use const to declare block-scoped variables that cannot be redfined; if you try to change the value of a const after declaring it, you’ll get a syntax error!

Questions for later:

  • Does the JavaScript interpreter actually create a bunch of functions to implement block scope with let? Or is something else happening under the hood? Or let me rephrase my question this way: if I were to write my own compiler (something I’m slowly working on through NAND2Tetris), could I implement function scope and block scope in the same way, or would I need to do something very different for each type of scope? How much code could I recycle when implementing those features?

  • How exactly does const work with objects? If it makes the pointer a constant, what does that even mean?

  • How do pointers work in JavaScript, anyway? (Or in general, for that matter!)

Error-handling with Promises

After my friend walked me through some examples to teach me all about the let statement, I walked him through what I understood so far about Promise objects and we both read through “JavaScript Promises: An Introduction” by Jake Archibald and reviewed some of the example code from the HTTP chapter of “Eloquent JavaScript”. Before this study session, I had spent a little time learning about Promises before, and I’ve used them for my mob coding app and other experiments with the GitHub API, but I still didn’t feel comfortable with them yet.

I won’t detail everything we reviewed, but the part about error-handling was especially interesting to me! I was never sure of the difference between testPromise.then(success, failure); and testPromise.then(success).catch(failure); but now I definitely understand the difference!

In a nutshell: The former will either succeed or fail, but never both! The latter could potentially succeed and then, if that function throws an error, it would be caught with .catch(failure) – so it’s possible that both the success and fail callback functions would run! And if you do testPromise.(success, fail1).catch(fail2); then you can create a chain that runs the first fail callback function if the promise is rejected and the second fail callback function if the promise is fulfilled but the success function itself fails. Phew!

It’s easier to show in a diagram, so I drew it in my friend’s notebook while he tested the code:

Promises error handling sketch

The sample code that we came up with looked a lot like this:

function testPromise(success) {
  return new Promise(function(fulfill, reject){
    setTimeout(function() {
      if (success) {
        fulfill();
      } else {
        reject(Error("Promise failed!"));
      }
    }, 1000);
  });
}

function succeedWithItsOwnError() {
  console.log("Fulfilled successfully!");
  throw "Error occurred in the success callback!";
}

function handleFail1(error) {
  console.log("handleFail1 called with error: " + error);
}

function handleFail2(error) {
  console.log("handleFail2 called with error: " + error);
}

// VERSION 1:
  // Will run succeedWithItsOwnError() -- NOTE UNCAUGHT ERROR!
  testPromise(true).then(succeedWithItsOwnError, handleFail1);

  // Will run handleFail1()
  testPromise(false).then(succeedWithItsOwnError, handleFail1);

// VERSION 2:
  // Will run succeedWithItsOwnError() AND handleFail1() -- YAY, ERROR CAUGHT!
  testPromise(true).then(succeedWithItsOwnError).catch(handleFail1);

  // Will run handleFail1(), same as in version 1
  testPromise(false).then(succeedWithItsOwnError).catch(handleFail1);

// VERSION 3:
  // Will run succeedWithItsOwnError() AND handleFail2() -- YAY, ERROR CAUGHT with handleFail2()!
  testPromise(true).then(succeedWithItsOwnError, handleFail1).catch(handleFail2);

  // Will run handleFail1() , same as in version 1 and version 2
  testPromise(false).then(succeedWithItsOwnError, handleFail1).catch(handleFail2);

I think that code speaks for itself pretty well, especially alongside the diagram I drew for myself. Now I finally feel somewhat comfortable with more complicated Promise chains like this one from “JavaScript Promises: An Introduction” (also be sure to see his corresponding flowchart here):

asyncThing1().then(function() {
  return asyncThing2();
}).then(function() {
  return asyncThing3();
}).catch(function(err) {
  return asyncRecovery1();
}).then(function() {
  return asyncThing4();
}, function(err) {
  return asyncRecovery2();
}).catch(function(err) {
  console.log("Don't worry about it");
}).then(function() {
  console.log("All done!");
});

Crazy stuff! From there, my friend and I moved on to reviewing that cool async/await fanciness.

Notes on async/await functions

Next we read over part of “Async functions - Making promises friendly”, the next article by Jake Archibald. We took an example of a chain of promises and converted it to an async function to see how much more readable it became, and then we experimented with how to make the code run in parallel instead of in sequence. We got stuck on one bug for a while, which I wanted to make a note of here.

Lesson learned: If you want to access a property from an object returned by a promise, you still need to await the promise object itself! Here’s an example:

// Run these promises in parallel
let person1 = getJSON("person1.json");
let person2 = getJSON("person2.json");

// This WILL NOT work!!! (Console just shows a Promise object!)
console.log(await person1.name);

// But this WILL work:
person1 = await person1;
console.log(person1.name);

Other new JavaScript features for next time

We touched on some other topics briefly, but I didn’t take notes on everything so I’ll just briefly list them here:

  • We learned about the for...of loop, which is like for...in but for arrays! (We barely scratched the surface of this one, though.)

  • We reviewed arrow functions again, which not only offer a convenient shorthand syntax but also fix some annoying scope issues with the this keyword!

  • I really want to learn more about generators next, which seem to act like functions that can be paused and resumed. How weird and interesting!

  • What are the other ways to use async/await in parallel? I briefly reviewed some examples that used arrays or objects to make that work, but I didn’t really dig into it yet.

Robotics workshop with Acrobotic

I assembled my first robot at tonight’s meetup group taught by my friends from Acrobotic! I didn’t get to finish it, but that will be a fun side project for later! Here are some photos from the workshop:

Wrapping up for the day: After that, I had time for a quick rock climbing session with another friend, which I really needed! Every day this week, I’d like to do some sort of activity to tire myself out, because it really helps with managing my anxiety and it’s fun, too! (I didn’t want to dwell on it, but I actually started out the day today fighting off a panic attack, feeling unprepared to start another work week after a short break visiting my family for Father’s Day. But by reaching out to people and making time for exercise, I managed to feel much better – and much more quickly than usual!) After rock climbing, I went back to my usual coworking space and chatted with coworking friends and did some more math practice problems on Khan Academy until I was starting to fall asleep. All in all, today was a good day.