Coding Journal

Jordan Vidrine

Daily Coding Journal

An experienced business owner + musician with a demonstrated history of management in the Energy sector and an insatiable appetite for learning new and complex skills. Transitioning from an ownership role & seeking employment in Web Development as a Jr Full Stack Developer.

July 9th 2019

Working from a great coffee shop in Lafayette today. Starting off the day reading through the asynchronous programming chapter in Eloquent Javascript. I feel fairly confident in what async functions are and how they work, but I want to read through this chapter to solidify any loose thinking in regards to that.

Chapter 11 Async - Eloquent Javascript

The author has a sense of humor. We will be learning the ins and outs of async by writing networking functions for a fictitious crow colony that has learned how to use javascript and communicate from nest to nest by sending signals to and from each other…. interesting.

Callbacks

One approach to async programming is to make functions that perform a slow action take a callback argument to run when the code is finished working. (We learned about this in the NodeJS course we are currently working through.) As we learned previously, this can get convoluted quickly when wanting to perform more than one async operation. The example below has two callbacks. Any more and this could lead to hard to lots of nesting and hard to read code. IE.

import {bigOak} from "./crow-tech";

bigOak.readStorage("food caches", caches => { // first callback
  let firstCache = caches[0];
  bigOak.readStorage(firstCache, info => { // second nested callback
    console.log(info);
  });
});

Promises

Here is where promises come in and are very handy! A Promise is an object that represents a future event and is used in place of a callback.

A promise is an asynchronous action that may complete at some point and produce a value. It is able to notify anyone who is interested when its value is available.

The easiest way to call a promise is to call Promise.resolve. This function will ensure the value you give it will be wrapped in a promise that immediately finishes with the value you gave it as its result.

let promiseFive = Promise.resolve(5);
promiseFive.then(value => console.log(`Got ${value}`))
// Got 5

To get the result of the promise, you call the .then method with a callback. This will register that callback to run when the promise is resolved. and produces a value.

Something I didnt know: You can call .then multiple times on a single promise, and they will be called, even if you add them after the promise has already resolved (finished).

let promiseWait = new Promise((resolve,reject) => {
  setTimeout(() => {
    resolve(5)
  },10000)
})

promiseWait.then(val => console.log(`got ${val + 5}`))
// after 10 seconds
// got 10

promiseWait.then(val => console.log(`got ${val + 10}`))
// no waiting since promise is resolved with 5
// got 15

This seems valuable to me because you can get data and store it in a variable as a promise. Once that promise resolves, you can use that same data on multiple other functions or calculations.

If you use the return statement in a .then method, it will return another promise resolved with whatever functions or calculations you performed on it, or, if whatever you manipulate it with returns a promise, it returns a promise, waits for that to be resolved, and then resolves to its result.

let promiseWait = new Promise((resolve,reject) => {
  setTimeout(()=>{
    resolve(15)
  },10000)
}).then(value => {
    console.log(`Value = ${value} from first promise`)
    return new Promise((resolve,reject) => {
      setTimeout(() => {
        resolve(value+5)
      },5000)
    })
}).then(newVal => console.log(`Value = ${newVal} from second promise`))

// After 10 seconds
// Value = 15 from first promise
// After 5 More seconds
// Value = 20 from second promise

I like the following description from the book:

It is useful to think of promises as a device to move values into an asynchronous reality. A normal value is simply there. A promised value is a value that might already be there or might appear at some point in the future. Computations defined in terms of promises act on such wrapped values and are executed asynchronously as the values become available.

Failure

Async programs need a failure option to check for any errors caused during the callback processes. With the callback method this is difficult and results in more lines of code and if/else statements to be used.

With Promises though this becomes a great deal easier. They can either be resolved, or rejected. If resolved, the result is passed to the next .then method. If rejected however, all .then methods are ignored after the failure, and the promise will jump to the .catch method to handle the error. .catch Is very mich like .then in that it returns a new promise which resolves to the original promises value if it resolves normally, otherwise it resolves to the value of the .catch handler.

As a shorthand, .then also accepts a rejection handler as a second argument, alongside the resolve function, which it can use to reject the new promise. Here is an example from the book of what a chain can look like.

new Promise((_, reject) => reject(new Error("Fail")))
  .then(value => console.log("Handler 1"))
  .catch(reason => {
    console.log("Caught failure " + reason);
    return "nothing";
  })
  .then(value => console.log("Handler 2", value));
// → Caught failure Error: Fail
// → Handler 2 nothing

What happens here is the promise immediately rejects, passing over the .then method and sending the .catch method an Error("Fail"). The .catch method logs this and its reason to the console and returns a value string of “Nothing”. When something is returned from a .catch or .then method, it is returned as a promise. Since this promise is resolved with the str of ‘nothing’ the next .then method can handle it. It then logs ‘Handler 2’ + the resolved promise value of ‘nothing’ to the console.

Javascript can also detect when errors are not handled. If a promise is rejected without a call to .catch javascript will throw an error to the console.

Importance of exception handling

It is also important to handle exceptions with Promises. What if the network is down, and our resolve or reject doesnt return anything? What if a function name was typed or misspelled? We should handle this issue with a try / catch statement.

promiseErrorTest = () => { return new Promise((resolve,reject) => {
  let rndm = Math.floor(Math.random()*100)
  setTimeout(() => {
    if (rndm > 50) {
      resolve('Resolved Promise')
    } else {
      reject(new Error('Rejected!'))
    }
  }, 5000)
})}

tryPromise = () => {
  try {
    promiseErrorTest().then(val=>console.log(val),
                            rejection=>console.log(rejection))
  } catch (e) {
    console.log(`Failure: ${e}`)
  }
}

tryPromise()
// wait 5 seconds then randomly resolve or rejects
// 'Resolved Promise' or 'Error: Rejected!'

// What if we misspelled promiseErrorTest in the tryPromise function?
// The catch (e) part of the try catch statement will CATCH this issue and return
// Failure: ReferenceError: promiseErrorTet is not defined

Collections of Promises

When working with a collection of Promises running at the same time, the Promise.all function is very useful. It returns a promise that waits for all of the promises in an array to resolve and then resolves to an array of the values produced. If any promise is rejected, the result of Promise.all is itself rejected. Lets test this out. First, lets create a fake promise.

let fakePromise = (val) => {
  return new Promise((resolve,reject) => {
    setTimeout(()=>{
      let rndm = Math.floor(Math.random()*10)
      if (rndm % 2 == 0) {
        resolve(val)
      }
      reject('Promise Didnt Work')
    },3000)
  })
}

Then, lets create a Promise.all call using a couple of the fake promises.

Promise.all([
  fakePromise(1),
  fakePromise(2),
  fakePromise(3)
]).then(results => console.log(results))

// uncaught exception: Promise Didnt Work

Hmm. So it seems that the Promise.all ONLY returns when every single proimise is fulfilled. Although cool, it seems a little useless if we want to know what failed and what didnt. To help we can attach a catch statement to each fakePromise() call like so:

Promise.all([
  fakePromise(1).catch(e => e),
  fakePromise(2).catch(e => e),
  fakePromise(3).catch(e => e)
]).then(results => console.log(results))

// Array(3) [ 1, "Promise Didnt Work", 3 ]

I feel like I would want to know what promises worked and which ones didnt if I were calling Promise.all() on a list of promises. I could then use this information to decide what to do next in a program.

I kept playing with this a little and wanted to write something that would filter through the returned array for only promises that were resolved. Here is something I came up with. Not sure if this is correct, but it does work.

Promise.all([
  fakePromise(1).then(
  (val)=> {return {resolved: true, val}},
  (e) => {return {resolved: false, error: e}}
  ),
  fakePromise(2).then(
    (val)=> {return {resolved: true, val}},
    (e) => {return {resolved: false, error: e}}
  ),
  fakePromise(3).then(
    (val)=> {return {resolved: true, val}},
    (e) => {return {resolved: false, error: e}}),
]).then(results => console.log(results.filter(promise => {return promise.resolved})))

// Promise { <state>: "pending" }

// [
// {resolved: true, val: 1 },
// {resolved: true, val: 2}
// ]

After these sections the book got a little lofty and used some examples that really threw me off course. This has happened every now and again in this book but I still enjoy it.

Async / Await

Javascript allows us to use pseudo synchronous code to describe asynchronous functions. An async function is a function that implicitly returns a promise and that can, in its body, await other promises in a way that looks synchronous. An async function is marked by the word async before the function keyword. Methods can also be made async by writing async before their name.

Inside of an async function you can use the word await in front of an expression to wait for a promise to resolve before moving onto the next line of code. Mimicking the functionality of a synchronous function, while remaining asynchronous.

For basic code, is is smart to use async/await with promises as it leads to an easier to read and understand code.

var asyncExample = async () => {
  try {
    let fakeValue1 = await fakePromise(1);
        console.log(fakeValue1)
  } catch (e) {
      console.log(e)
}

// if promise fulfills --> 1
// if it fails --> 'Promise Didnt Work'

For simple stuff, this seems a lot easier to read than using lots of .then and .catch statements. The ease of putting what you want to try to do in a try/catch is easier to understand in my opinion.

Generators

It is here the author also introduces Javascript’s Generator functions. We looked at something similar, the ‘Iterator’ back in chapter 6, but the Generator is much more straightforward and easy to digest. To make a function a Generator you add * to the function declaration like so. Each time the .next() method is called on the function, it runs through its code until it hits yield Here is an example from mdn website:

function* makeRangeIterator(start = 0, end = 100, step = 1) {
    for (let i = start; i < end; i += step) {
        yield i;
    }
}

let example = makeRangeIterator(1,10,1)

example.next() // {value: 1, done: false}
example.next() // {valie: 2, done: false}

Once yield is no longer reached, done is set to false and value will always be undefined.

The Event Loop

Back to promises…

Asynchronous behavior happens on it’s own empty callback. Promises always resolve or reject as a new event on the async event stack.

Promise.resolve("Done").then(console.log);
console.log("Me first!");
// → Me first!
// → Done

Summary

Whew! What a chapter. I’m glad I read through it and worked through a lot of the examples in chromes dev tools. This really solidified how Promises work. Especially Promise.all().

Back to blog...