Larry Price

And The Endless Cup Of Coffee

Promise You'll Call Back

| 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.

Using D-Bus Signals in Python

| Comments

This is the third in a series of blog posts on creating an asynchronous D-Bus service in python. For the inital entry, go here. For the previous entry, go here

Last time we transformed our base synchronous D-Bus service to include asynchronous calls in a rather naive way. In this post, we’ll refactor those asynchronous calls to include D-Bus signals; codewise, we’ll pick up right where we left off after part 2: https://github.com/larryprice/python-dbus-blog-series/tree/part2. Of course, all of today’s code can be found in the same project with the part3 tag: https://github.com/larryprice/python-dbus-blog-series/tree/part3.

Sending Signals

We can fire signals from within our D-Bus service to notify clients of tasks finishing, progress updates, or data availability. Clients subscribe to these signals and act accordingly. Let’s start by changing the signature of the slow_result method of RandomData to be a signal:

random_data.py
1
2
3
4
# ...
@dbus.service.signal("com.larry_price.test.RandomData", signature='ss')
def slow_result(self, thread_id, result):
    pass

We’ve replaced the context decorator with a signal, and we’ve swapped out the guts of this method for a pass, meaning the method will call but doesn’t do anything else. We now need a way to call this signal, which we can do from the SlowThread class we were using before. When creating a SlowThread in the slow method, we can pass in this signal as a callback. At the same time, we can remove the threads list we used to use to keep track of existing SlowThread objects.

random_data.py
1
2
3
4
5
6
7
8
9
10
11
12
13
class RandomData(dbus.service.Object):
    def __init__(self, bus_name):
        super().__init__(bus_name, "/com/larry_price/test/RandomData")

        random.seed()

    @dbus.service.method("com.larry_price.test.RandomData",
                         in_signature='i', out_signature='s')
    def slow(self, bits=8):
        thread = SlowThread(bits, self.slow_result)
        return thread.thread_id

    # ...

Now we can make some updates to SlowThread. The first thing we should do is add a new parameter callback and store it on the object. Because slow_result no longer checks the done property, we can remove that and the finished event. Instead of calling set on the event, we can now simply call the callback we stored with the current thread_id and result. We end up with a couple of unused variables here, so I’ve also gone ahead and refactored the work method on SlowThread to be a little cleaner.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# ...

class SlowThread(object):
    def __init__(self, bits, callback):
        self._callback = callback
        self.result = ''

        self.thread = threading.Thread(target=self.work, args=(bits,))
        self.thread.start()
        self.thread_id = str(self.thread.ident)

    def work(self, bits):
        num = ''

        while True:
            num += str(random.randint(0, 1))
            bits -= 1
            time.sleep(1)

            if bits <= 0:
                break

        self._callback(self.thread_id, str(int(num, 2)))

And that’s it for the service-side. Any callers will need to subscribe to our slow_result method, call our slow method, and wait for the result to come in.

Receiving Signals

We need to make some major changes to our client program in order to receive signals. We’ll need to introduce a main loop, which we’ll spin up in a separate thread, for communicating on the bus. The way I like to do this is with a ContextManager so we can guarantee that the loop will be exited when the program exits. We’ll move the logic we previously used in client to get the RandomData object into a private member method called _setup_object, which we’ll call on context entry after creating the loop. On context exit, we’ll simply call quit on the loop.

client
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
# Encapsulate calling the RandomData object on the session bus with a main loop
import dbus, dbus.exceptions, dbus.mainloop.glib
import threading
from gi.repository import GLib
class RandomDataClient(object):
    def __enter__(self):
        self._setup_dbus_loop()
        self._setup_object()

        return self

    def __exit__(self, exc_type, exc_value, traceback):
        self._loop.quit()
        return True

    def _setup_dbus_loop(self):
        dbus.mainloop.glib.DBusGMainLoop(set_as_default=True)
        self._loop = GLib.MainLoop()

        self._thread = threading.Thread(target=self._loop.run)
        self._thread.start()

    def _setup_object(self):
        try:
            self._bus = dbus.SessionBus()
            self._random_data = self._bus.get_object("com.larry-price.test",
                                                     "/com/larry_price/test/RandomData")
        except dbus.exceptions.DBusException as e:
            print("Failed to initialize D-Bus object: '%s'" % str(e))
            sys.exit(2)

We can add methods on RandomDataClient to encapsulate quick and slow. quick is easy - we’ll just return self._random_data.quick(bits). slow, on the other hand, will take a bit of effort. We’ll need to subscribe to the slow_result signal, giving a callback for when the signal is received. Since we want to wait for the result here, we’ll create a threading.Event object and wait for it to be set, which we’ll do in our handler. The handler, which we’ll call _finished will validate that it has received the right result based on the current thread_id and then set the result on the RandomDataClient object. After all this, we’ll remove the signal listener from our bus connection and return the final result.

client
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
class RandomDataClient(object):
    # ...

    def quick(self, bits):
        return self._random_data.quick(bits)

    def _finished(self, thread_id, result):
        if self._thread_id == self._thread_id:
            self._result = result
            self._done.set()

    def slow(self, bits):
        self._done = threading.Event()
        self._thread_id = None
        self._result = None

        signal = self._bus.add_signal_receiver(path="/com/larry_price/test/RandomData", handler_function=self._finished,
                                               dbus_interface="com.larry_price.test.RandomData", signal_name='slow_result')
        self._thread_id = self._random_data.slow(bits)
        self._done.wait()
        signal.remove()

        return self._result

Now we’re ready to actually call these methods. We’ll wrap our old calling code with the RandomDataClient context manager, and we’ll directly call the methods as we did before on the client:

client
1
2
3
4
5
6
7
8
# ...

# Call the appropriate method with the given number of bits
with RandomDataClient() as client:
    if args.slow:
        print("Your random number is: %s" % client.slow(int(args.bits)))
    else:
        print("Your random number is: %s" % client.quick(int(args.bits)))

This should have feature-parity with our part 2 code, but now we don’t have to deal with an infinite loop waiting for the service to return.

Next time

We have a working asynchronous D-Bus service using signals. Next time I’d like to dive into forwarding command output from a D-Bus service to a client.

As a reminder, the end result of our code in this post is MIT Licensed and can be found on Github: https://github.com/larryprice/python-dbus-blog-series/tree/part3.