Larry Price

And The Endless Cup Of Coffee

Async and Await - a New Promise

| Comments

In my last post, I discussed the ES2015 concept of a Promise. A Promise provides a simplified mechanism for performing asynchronous work in JavaScript without using the classic setTimeout-callback approach. Seeing as it’s been about 4 months since my previous post, a new asynchronous concept is on the rise as part of the ES2017 specification: async and await.

I became aware of async and await after reading David Walsh’s blog, at which point I disregarded the new features as being “too soon” and “not different enough” from a Promise to warrant a second thought. Then, yesterday, I used them, and my life was, once again, forever changed.

await is used to essentially wait for a Promise to finish. Instead of using a callback with a then clause, await allows you to perform the action and/or store the result like you’re within a synchronous function.

async is a keyword identifier used on functions to specify that that function will use await. Try to call await in a function not labeled as async and you’re going to have a bad time. Any async function returns a Promise.

Let’s see an example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
function getFirstName() { return Promise.resolve('Charles'); }
function getMiddleName() { return Promise.resolve('Entertainment'); }
function getLastName() { return Promise.resolve('Cheese'); }

async function getName() {
  const first = await getFirstName();
  const middle = await getMiddleName();
  const last = await getLastName();

  return `${first} ${middle} ${last}`;
}

getName().then((name) => {
  console.log(name);
});

console.log('My next guest needs no introduction:');

// Result:
//   My next guest needs no introduction:
//   Charles Entertainment Cheese

We have three functions which each return a Promise, and an async function which calls those functions sequentially and uses the results to construct a string. We call the getName function (which is async and therefore returns a Promise) and log the results. Our last command logs a special message. Due to the asynchronous nature of the getName function, our special message is logged first, and then the result of getName.

This comes in handy when you’re depending on the results of a Promise to do some work or pass into another asynchronous call. But, in the case of our getName function above, we could be getting all three of the names at once. This calls for the brilliant Promise.all method, which can also be used with async. Let’s modify our sub-name functions to all use async and then fetch them all at once:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
async function getFirstName() { return 'Charles'; }
async function getMiddleName() { return 'Entertainment'; }
async function getLastName() { return 'Cheese'; }

async function getName() {
  const names = await Promise.all([getFirstName(), getMiddleName(), getLastName()]);

  return `${names[0]} ${names[1]} ${names[2]}`;
}

getName().then((name) => {
  console.log(name);
});

console.log('My next guest needs no introduction:');

// Result:
//   My next guest needs no introduction:
//   Charles Entertainment Cheese

Since an async function just returns a Promise, we can directly use (and even inter-mix) async functions inside Promise.all, and the results come back in an ordered array.

OK, what if we want to fire off some long-running task and do some other work in the meantime? We can defer our use of await until after we’ve performed all the intermediate work:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
async function getFirstName() { return 'Charles'; }
async function getMiddleName() { return 'Entertainment'; }
async function getLastName() { return 'Cheese'; }

async function getName() {
  const first  = getFirstName();  // first, middle, and last will all
  const middle = getMiddleName(); // be pending Promises at this
  const last   = getLastName();   // point, to be resolved in time

  const title = Math.random() > .5 ? 'Sr.' : 'Esq.';

  return `${await first} ${await middle} ${await last}, ${title}`;
}

getName().then((name) => {
  console.log(name);
});

console.log('My next guest needs no introduction:');

// Result will be quasi-random:
//   My next guest needs no introduction:
//   Charles Entertainment Cheese, (Esq.|Sr.)

This example reiterates that you can use async functions just like you would a Promise, but with the added benefit of using await to wait for the results when necessary.

I know what you’re thinking: “All these positives, Larry! Is there nothing negative about async/await?” As always, there are a couple of pitfalls to using these functions. The biggest nuisance for me is the loss of the catch block when converting from a Promise chain. In order to catch errors with async/await, you’ll have to go back to traditional try/catch statements:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
async function checkStatus() { throw 'The Cheese is displeased!'; }

async function checks() {
  try {
    await checkStatus();
    return 'No problems.';
  } catch (e) {
    return e;
  }
}

checks().then((status) => {
  console.log(status)
})
console.log('Current status:');

// Result will be quasi-random:
//   Current status:
//   The Cheese is displeased!

The only other real downside is that async and await may not be fully supported in your users' browsers or your version of Node.JS. There are plenty of ways to get around this with Babel and polyfills, but, to be honest, I dedicated a large chunk of time yesterday afternoon to upgrading all of our libraries and babel versions to get this to work properly everywhere. Your mileage may vary, and, if you’re reading this 6 months from when it was posted, I’m sure it will be available by default in any implementations of ECMAScript.

Promise You'll Call Back: A Guide to the Javascript Promise Class

| Comments

This article introduces the Javascript Promise class, and how to use a Promise to perform asynchronous work. At the end of this post, you’ll have been exposed to the most important components of the Promise API.

Introduced in the ES2015 specification, MDN dryly describes a Promise as:

The Promise object represents the eventual completion (or failure) of an asynchronous operation, and its resulting value.

But… what exactly does that entail? How does it differ from just using callbacks?

Let’s start with a simple example. If I want to perform an operation asynchronously, traditionally I would use setTimeout to do work after the main thread has finished and use a callback parameter to let the caller utilize the results. For example:

1
2
3
4
5
6
7
8
9
10
11
12
13
const someAsyncTask = (after) => {
  return setTimeout(() => {
    after('the task is happening');
  }, 0);
};

console.log('before calling someAsyncTask');

someAsyncTask((result) => {
  console.log(result);
});

console.log('after calling someAsyncTask');

Try running this yourself with node, and you’ll see that ‘before…’ and ‘after…’ are printed followed by ‘the task is happening’.

This is perfectly valid code, but it’s just so unnatural to handle asynchronous tasks this way. There’s no standard to which parameter should be the callback, and there’s no standard to what arguments will be passed back to a given callback. Let’s take a look at the same situation using the new Promise class:

1
2
3
4
5
6
7
8
9
10
11
const someAsyncTask = () => {
  return Promise.resolve('the task is happening');
};

console.log('before calling someAsyncTask');

someAsyncTask().then((result) => {
  console.log(result);
});

console.log('after calling someAsyncTask');

Let’s walk through this. In someAsyncTask, we’re now returning a call to Promise.resolve with our result. We call then on the result of someAsyncTask and then handle the results. Promise.resolve is returning a resolved Promise, which is run asynchronously after the main thread finishes its initial work (the final console.log, in this case).

Immediately, this feels a lot cleaner to me, but this is a really simple example.

Think about a situation where you need to perform multiple asynchronous callbacks that each depend on the results of the last callback. Here’s an example implementation using callbacks;

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
const getFirstName = (callback) => {
  return setTimeout(() => {
    callback('Harry');
  }, 0);
};

const getLastName = (callback) => {
  return setTimeout(() => {
    callback('Potter');
  }, 0);
};

const concatName = (first, last, callback) => {
  return setTimeout(() => {
    callback(`${first} ${last}`);
  }, 0);
}

getFirstName((first) => {
  getLastName((last) => {
    concatName(first, last, (fullname) => {
      console.log(fullname);
    });
  });
});

I think we can all agree that this is not friendly code. What makes a Promise truly special is its natural chainability. As long as we keep returning Promise objects, we can keep calling then on the results:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
const getFirstName = (callback) => {
  return Promise.resolve('Harry');
};

const getLastName = (callback) => {
  return Promise.resolve('Potter');
};

const concatName = (first, last, callback) => {
  return Promise.resolve(`${first} ${last}`);
}

getFirstName().then((first) => {
  return getLastName().then((last) => {
    return concatName(first, last);
  });
}).then((fullname) => {
  console.log(fullname);
});

Since concatName is dependent on the result of both getFirstName and getLastName, we still do a little bit of nesting. However, our final asynchronous action can now occur on the outside of the nesting, which will take advantage of the last returned result of our Promise resolutions.

Error handling is another can of worms in callbacks. Which return value is the error and which is the result? Every level of nesting in a callback has to either handle errors, or maybe the top-most callback has to contain a try-catch block. Here’s a particularly nasty example:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
const getFirstName = (callback) => {
  return setTimeout(() => {
    if (Math.random() > 0.5) return callback('Sorry, firstName errored');
    callback(null, 'Harry');
  }, 0);
};

const getLastName = (callback) => {
  return setTimeout(() => {
    if (Math.random() > 0.5) return callback('Sorry, lastName errored');
    callback(null, 'Potter');
  }, 0);
};

const concatName = (first, last, callback) => {
  return setTimeout(() => {
    if (Math.random() > 0.5) return callback('Sorry, fullName errored');
    callback(null, `${first} ${last}`);
  }, 0);
}

getFirstName((err, first) => {
  if (err) console.error(err); // no return, will fall through despite error
  getLastName((err, last) => {
    if (err) return console.error(err);
    concatName(first, last, (err, fullname) => {
      if (err) return console.error(err);
      console.log(fullname);
    });
  });
});

Every callback has to check for an individual error, and if any level mishandles the error (note the lack of a return on error after getFirstName), you’re guaranteed to end up with undefined behavior. A Promise allows us to handle errors at any level with a catch statement:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
const getFirstName = () => {
  if (Math.random() > 0.5) return Promise.reject('Sorry, firstName errored');
  return Promise.resolve('Harry');
};

const getLastName = () => {
  if (Math.random() > 0.5) return Promise.reject('Sorry, lastName errored');
  return Promise.resolve('Potter');
};

const concatName = (first, last) => {
  if (Math.random() > 0.5) return Promise.reject('Sorry, fullName errored');
  return Promise.resolve(`${first} ${last}`);
}

getFirstName().then((first) => {
  return getLastName().then((last) => {
    return concatName(first, last);
  });
}).then((fullname) => {
  console.log(fullname);
}).catch((err) => {
  console.error(err);
});

We return the result of Promise.reject to signify that we have an error. We only need to call catch once. Any then statements from unresolved promises will be ignored. A catch could be inserted at any nesting point, which could give you the ability to continue the chain:

1
2
3
4
5
6
7
8
9
10
11
12
13
// ...

getFirstName().then((first) => {
  return getLastName().then((last) => {
    return concatName(first, last);
  }).catch((err) => {
    return concatName(first, 'Houdini');
  });
}).then((fullname) => {
  console.log(fullname);
}).catch((err) => {
  console.error(err);
});

So far, we’ve been returning Promise objects using resolve and reject, but there’s also the ability to define our own Promise objects with their own resolve and reject methods. Updating the getFirstName variable:

1
2
3
4
5
6
7
const getFirstName = () => {
  return new Promise((resolve, reject) => {
    if (Math.random() > 0.5) return reject('Sorry, firstName errored');
    return resolve('Harry');
  });
}
// ...

We can also run our asynchronous tasks without nesting by using the Promise.all method:

1
2
3
4
5
6
7
8
9
// ...

Promise.all([getFirstName(), getLastName()]).then((names) => {
  return concatName(names[0], names[1]);
}).then((fullname) => {
  console.log(fullname);
}).catch((err) => {
  console.error(err);
});

Give Promise.all a list of promises and it will call them (in some order) and return all the results in an array (in the order given) as a resolved Promise once all given promises have been resolved. If any of the promises are rejected, the entire Promise will be rejected, resulting in the catch statement.

Sometimes you need to run several methods, and you only care about the first result. Promise.race is similar to Promise.all, but only waits for one of the given promises to return:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
const func1 = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => resolve('func1'), 5*Math.random());
  });
}

const func2 = () => {
  return new Promise((resolve, reject) => {
    setTimeout(() => resolve('func2'), Math.random());
  });
}

Promise.race([func1(), func2()]).then((name) => {
  console.log(name);
}).catch((err) => {
  console.error(err);
});

Sometimes, ‘func1’ will be printed, but most of the time ‘func2’ will be printed.

…And that’s the basics! Hopefully, you have a better understanding of how a Promise works and the advantages provided over traditional callback architectures. More and more libraries are depending on the Promise class, and you can really clean up your logic by using those methods. As Javascript continues to evolve, hopefully we find ourselves getting more of these well-designed systems to make writing code more pleasant.

Demystifying Public Speaking

| Comments

The Gist

Public speaking makes me nervous, and I’m not alone. A crowd of people is listening to your stutters, nit-picking your errors, and judging your clothing. No one is immune from the fear of public speaking. What can you do about it? Armed with Lara Hogan’s Demystifying Public Speaking, we can learn how to make public speaking a bit less stressful. There is no complete answer, but this book is full of tips and guidance for speaking engagements of any size and gravitas.

Takeaways

Need ideas for public speaking? Take advantage of the work you do every day. Prepare a presentation for the tough code you wrote last week, the library you found, the Agile processes you use, or how you set up your workstation, favorite tool, or cloud service.

Start small. Run the topic by your coworkers with a rough outline. Run it by your spouse to get an outside perspective. You can tweak your ideas based on the feedback, and then move on to bigger venues. Do a lunch and learn, a lightning talk, or a local meetup.

Your end goal does not have to be a conference. Conferences can be huge events with many attendees, and can be extremely daunting. Many people only go to conferences for the big names, and your talk might be more easily forgotten amongst all the ultra-hyped celebrity talks.

Then again, if that’s what you’re into, you could become the celebrity after doing a few conference talks. If you do well at one or two conferences, there’s a good chance you’ll start getting invited to more conferences. These conferences might want you to rehash your past talk (score! minimal effort!), give you a topic, or hand you the reigns to get creative.

Your audience wants you to do well. It’s a common misconception that your audience is rooting against you. They want to learn, and they want to believe that what you’re telling them is worthwhile. If you make a mistake, you don’t need to be embarrassed: everyone knows it’s hard to go on-stage in front of a group of people. Just try to correct yourself and move on.

Always include some levity in your presentation. A joke or a cat picture can help reengage an audience that may be succumbing to fatigue. Ask a silly or surprising question, maybe even going so far as to ask for some audience participation.

Presentations with lots of imagery are great, but your presentation style doesn’t have to follow any conventions. Some people are comfortable getting their cues from their notes and images, but others may prefer more traditional header and bullet point slides.

If there’s going to be a Q&A section, have your coworkers or peers hit you with some potential questions. Maybe you can beef up parts of your presentation that were misinterpreted or underrepresented.

It’s okay to say “I don’t know” during a Q&A session. You just laid down a lot of knowledge on your audience, but that doesn’t mean you have to know all the answers. Furthermore, if someone “stumps” you during a Q&A section, just admit it and move on. There’s always that one guy who asks a “question” that he already knows the answer to to make himself appear intelligent. Ignore that guy. He’s got issues. Just say “OK” and move on to the next question.

Do what’s comfortable for you. Read directly from your notes. Put comforting reminders in your slides, like pictures of your cats or Superman squashing fascism. Use “wizard hands” or other embarrassing hand gestures. Let your personality come out, or invent a completely separate stage persona to assume while you’re presenting. All that matters is that you accomplish your task of dropping some knowledge bombs on your intended audience.

Remember: you’re the expert on this topic. If you weren’t, you wouldn’t be able to put together that presentation to begin with. Your presentation was chosen because the organizer(s) had confidence in you, your ability, and your knowledge. The audience members are there because they find meaning in your topic and believe you’re the right person to transfer that information. You’re in control.

Action Items

I really only have one action item from this book:

  • Do some public speaking!

I’ve moved to a new city this summer, and I’m starting to actively seek out local meetup groups. My goal is to find the right opportunity and the courage to participate in some lightning talks or possibly longer presentations.

Where's Larry?

| Comments

I’ve been on hiatus for a few months. Aside from the technical writing I usually do on this blog, I haven’t pushed much extracurricular code or read any programming books. So what have I been doing?

Warning: This is going to come off as a diary entry about my life and is not intended as a free, highly technical post about programming. Come back later for more of that.

Downhill

Old Job

In mid-April, Canonical went through a major focus shift and with it a major round of layoffs. The company sought to become a more profitable business, and no longer believed the innovative work being done around Unity 8, Mir, and convergence could lead to profitability, and instead decided to focus on snaps and the cloud. Most of us working on the projects I mentioned got the axe, as well as others in various other departments.

To quote Vonnegut: “So it goes.”

I understand the shift from a business perspective, but have many opinions about the layoff process and the future of Ubuntu. Ticket price is currently set at two whiskey drinks. Ubuntu is shifting to Gnome 3 by default in October, making the line between Ubuntu and Fedora a bit cloudier. The open-source community lost a unique desktop environment, an interesting (if ambitious) vision for the future, and a large number of contributors putting in at least 40 hours a week.

Wanderer

In the meantime, my wife had set a date to leave her job and go back to graduate school in another state. We needed to find a new place to live in the next couple months and sell our home in Indiana. Needless to say, losing 2/3 of our income added a little bit of stress to the situation.

A Matter of Health

Ever since I got back from FOSDEM in February, I was slowly losing weight. No matter how much I ate, the pounds continued to drop. After the layoff, my health deteriorated quickly. Going to bed each night, I could hear my heart beating way too fast. I got tired walking up the stairs. It literally hurt to sit on my bony behind. With the layoff, my health insurance was also gone unless I wanted to pay $500+ each month to “take advantage” of COBRA.

So it goes.

Uphill

New Job

Fortunately, I was able to find a new job fairly quickly. There are lots of companies looking for programmers right now, so I had my pick of interesting domains to apply to. I landed at a company called Illinois Rocstar (IR for short) as a Research Engineer, working on SBIR contracts for the Department of Energy and Department of Defense. The company is mostly non-traditional programmers, and I came in with a lot to offer as far as writing software, building applications, and designing processes. It’s a fairly unique experience to work in the world of science with co-workers who come from very different backgrounds. I work in an office again, but it’s an office of fewer than 20 people in the beautiful University of Illinois Research Park.

New Bill of Health

With new job on hand, I acquired new health insurance as quickly as possible and went to see a physician. The diagnosis: adult-onset diabetes. Over the past three months, I have made drastic changes to my diet and significantly improved my health. It’s amazing how carb-heavy the American diet is; added sugar pervades practically everything we eat. I currently make most of my own meals at home, and it has intensified my love of the culinary arts.

Migration

We found a new home in our new city (Champaign-Urbana, IL) quickly: it’s a 1913 arts & crafts style home with a tiny yard, big kitchen, and a basement. It’s walking distance to downtown, parks, and grocery stores. We adore this home and were really lucky to find it.

Flatlander

For a while, it seemed the pattern was all tragic events followed by a series of purely positive events. But the story is never quite that simple.

While my health has been slowly improving, my great-grandmother passed away naturally, and my parents revealed that they had been secretly dealing with some health issues of my father’s. Although I am getting better, I still have a ways to go before I could be described as fully healthy; but I’ve found a new physician in Champaign, and we’re working on it.

Although we found the new house in Champaign quickly, selling our home in Indiana is another story. With the housing market the way it is right now, every schmuck on the street was telling us we would sell our home the first weekend we listed. The reality is that it took about 80 days to find a buyer, and the closing date is 134 days after listing (still a couple weeks from the date I’m writing this post). This meant two mortgages for a couple months, which has forced us to stretch our dollar a bit. The good news, of course, is that I will no longer be a real estate mogul in a short couple of weeks.

Dear Journal

Enough of my sob story.

I’m just about ready to start looking for opportunities to get involved in programming outside of the office again. I have at least one mobile application in mind, I could blog forever about all the React I’ve done and scientific tools I’ve learned to use, and I’ll be looking for locals to start sharing my experience with.

Time to get back in the game.

On Gender Bias and Home Improvement

| Comments

Instead of a highly technical guide, today I have a short anecdote of some recent electrical work we did in the master bathroom.

I met my wife when I joined my high school robotics team. I was a hammer: with no singular skill that I wanted to focus on, I was thrown into painting, cutting, drilling, electrical, lifting, sanding, building the website, and even marketing. Funnily enough, I never actually worked on programming the robot. As far as I knew at the time, my wife was doing similar work with more focus on marketing and operating the robot.

Fast-forward 10 years. I do most of the indoor home improvement work (electrical, dry walling, painting), and our yard work is fairly balanced. A few weekends ago, we were swapping out the light fixtures in the master bathroom. Well, she was mostly holding the flashlight while I cursed at the previous owner’s hackwork. As we’re wrapping up the first fixture, she starts asking really basic electrical questions. Beginner’s questions.

This struck me as a little odd. I answer with a bit of sarcasm, mentioning that it’s the absolute basic stuff she should remember from being on the robotics team. As it turns out, the (all-male) mentor staff never really encouraged the girls to work directly on the robot. The girls were pushed toward things like “marketing” and operating the robot, presumably because it reflected well on the team to have female drivers/operators. I started to think back on our overlapping time on the team and… Come to think of it, the girls really were pushed into a less-technical experience while I was pushed towards the greasy work.

This was 10 years ago, so I would hope things are a bit different now as more girls are influenced to get interested in STEM in high school. It’s a shame that she wasn’t encouraged to take on the same tasks as the boys, and it’s left an obvious effect on her into adulthood.

So I started thinking about my parents. My father would include me as much as possible in fixing up the house. My sister, on the other hand, would generally not be included. Granted, she’s 4 years younger than me, but I was forced to help my dad do things from a very young age when I would have rather been playing video games than learning life lessons. My wife is the eldest of three daughters, and they also were never really invited to help with home improvement outside of yard work.

We may not have grown up in the most forward-thinking town, but our parents are not sexists. Our robotics mentors may have lived a bit in the past, but they intended no malice while directing students towards work. In both of these cases, the adults were following the same pattern they’ve seen for generations. The trick now is to break that pattern.

For the second light fixture in the master bathroom, I held the flashlight. I instructed my wife in removing and replacing the fixture, only intervening on stubborn bolts. After we finished, she thanked me. I felt a bit guilty for not letting her take the wheel for so long. My assumption had always been she had the skills but no desire to get her hands dirty. Turns out she only needed the opportunity to break the pattern.