Promises, Promises Pt. 1
The Art of Waiting Around

It has been said of history that it’s “just one damned thing after another.” (By the way, that’s the title of a fantastic book about time travel by Jodi Taylor, and I can’t recommend it enough). The way we tell the story of history is often very sequential. An archduke is shot in Sarajevo, the Central Powers start a war, yada, yada, yada. But this telling hides a very complicated web of events that encapsulates trade agreements, royal marriages, personal affronts, and a regressive genetic defect called the “Hapsburg Jaw”.

In web development, as well as history, we deal with a rather complicated set of systems that are talking to each other. Web servers, databases, APIs, web workers, calls to local storage, animations, and that blasted user who keeps pushing buttons. A lot of software assumes that someone will request a task and the rest of the world will just wait till that task is done, and change nothing in meantime. There may be contexts where that occurs, but web dev isn’t one of them.

There’s a term for this type of problem: “Asynchronous Programming”. It means that you don’t have everything happening in one long sequence. You might have things happening in parallel, or requests that might fail and need to be retried till they succeed, or even just really long and burdensome processes that, if we made the core process wait, would turn your app into a pretty brick. So I want to talk about one of the constructs that JavaScript developers use to solve this problem, and since I am a big proponent of understanding the “what” before the “how”, let’s talk nachos.

The Epic Tale of Chips & Cheese

Now, I’m not gonna tell you what you should have on your nachos (like pickled jalapeno if you’re into that sort of thing), but let’s say you want to make a simple batch of chips with melted cheddar cheese. And let’s assume that on your kitchen counter, you already have the perfect amount of chips and shreded cheese. All right, well this is just a pretty linear process. In fact, lets do it in JavaScript!

function makeNachos(pan, chips, shreddedCheese){
	var chipsOnPan = spread(chips, pan);
	var assembledNachos = spread(shreddedCheese, chipsOnPan);
	var cookedNachos = cook(assembledNachos)
	return cookedNachos;
}

Mmmmm. That’s some tasty JavaScript. And a nice linear process, with everything in order. Which is great. So it’s nacho time, and you’re ready to use your make nacho function. You go the cupboard in your kitchen, and reach for the chips, but oh no! Your scuzzy roommate has eaten them all! And your shredded cheese! You confront him, tears in your eyes, and in a rare moment of contrition, he agrees to go to the local convenience store to replace the poached foodstuff.

var suppliesInKitchen = {
	chips: null, cheese: null, // Blasted roomate!
	pan = myOldTrustyPan // At least I have this.
};

var isRoommateStillGone = true;

while(isRoomateStillGone){
	isRoommateStillGone = checkForRoommate();
}

var stuffFromStore = getStuffFromRoommate()

var nachos = makeNachos(
	suppliesInKitchen.pan,
	stuffFromStore.chips,
	stuffFromStore.shreddedCheese);

eat(nachos);

Now, this isn’t an entirely healthy approach. For however long your roommate is gone, you just keep staring at the door waiting for him to come back. Wouldn’t you rather be doing stuff? Listening to music? Practicing dance moves? I mean, live your life! Let’s create a function called sendRoommateFor() and attach an onReturn event listener that does something with stuffFromStore.

var suppliesInKitchen = {
	chips: null, cheese: null, // Blasted roomate!
	pan = myOldTrustyPan // At least I have this.
};

sendRoommateFor(["chips", "shreddedCheese"])
	.onReturn(function(stuffFromStore){
		var nachos = makeNachos(
			suppliesInKitchen.pan,
			stuffFromStore.chips,
			stuffFromStore.shreddedCheese);

		eat(nachos);
	});

Well, that’s better. You were able to break this into two distinct processes: 1) you sent your roommate for chips and shredded cheese, and then 2) when he came back, you started another process that made and ate the nachos. In the meantime, you were free to do whatever your heart desired, which may or may not have involved dancing around in the kitchen to Taylor Swift.

And now, Promises

There is a really easy-to-use, ready-made construct called Promises that has been getting more and more popular in the JavaScript world. JavaScript devs were using them first to deal with AJAX requests, but you can use them for all types of asynchronous behavior, including localStorage, animations, and more. A promise is just a very simple state machine. It has an initial state and two final states.

Once a promise has entered either the resolved or rejected state, it cannot change to one of the other states. It resolves with a value, and rejects with an error. And for the sake of argument, we can say that if a promise resolves with a certain type, it can be called a promise of that type (e.g. a promise that will resolve with a boolean is a “promise of type boolean”. You can then attach event handlers to the promises. Let’s make this whole “going to the store” thing a Promise (of the ES6 JavaScript variety).

var suppliesInKitchen = {
	chips: null, cheese: null, // Blasted roomate!
	pan = myOldTrustyPan // At least I have this.
};

// create Promise of type "{chips: ChipsObject, shreddedCheese: ShreddedCheeseObject}"
var roommatePromise = sendRoommateFor("chips", "shreddedCheese");

roommatePromise
	.then(
		// Success function
		function(stuffFromStore){
			var nachos = makeNachos(
				suppliesInKitchen.pan,
				stuffFromStore.chips,
				stuffFromStore.shreddedCheese);
			eat(nachos);
	}, 	);

Now, this doesn’t seem like much of an improvement over the last, but there are a lot of great combinations we can do here. Let’s say that you’re making nachos for your significant other, who is sitting in the TV room. She has asked for nachos, but she doesn’t need to know about this whole roommate drama. She’s not even concerned with how long it’s taking: she can wait forever for these nachos, but she wants nachos. We can basically hand her a nacho promise, and then she can eat them when they arrive. Now it looks like this:

var suppliesInKitchen = {
	chips: null, cheese: null, // Blasted roomate!
	pan = myOldTrustyPan // At least I have this.
};

var joel = {
	makeAndDeliverNachos: function(){
		// Get a promise of your shopping list
		var roommatePromise = roommate.sendFor(["chips, shreddedCheese"]);

		// Turn that into a promise of type "plate of nachos"
		var nachoPromise = roomatePromise.then(function(stuffFromStore){
			var nachos = makeNachos(
				suppliesInKitchen.pan,
				stuffFromStore.chips,
				stuffFromStore.shreddedCheese);
			return nachos;
		});

		// return that promise
		return nachoPromise;
	}	
};

var roommate = {
	sendFor: function(shoppingList){
		/* return promise of type {chips: Object, shreddedCheese: Object} */
	}
};

var significantOtherRequest = joel
	.makeAndDeliverNachos()
	.then(function(nachos){
		eat(nachos);
	});
	// which could be further reduced to:
	// joel
	//	.makeAndDeliverNachos()
	// 	.then(eat)

In a nutshell, that’s the “what” of promises. Promises are asynchronous values that can be produced, transformed, and combined. Because of this, it’s really easy to write different pieces of software that perform different functions on these promises:

Producer (Roommate) Generates the content (via http request, timeouts, etc.)
Transformer (Me) Takes a promise and converts the ingredients into something useful
Consumer (Significant Other) Does something with the transformed content, such as updating a view

One important principle of writing software is the “Single Responsibility Principle”, where each bit of software is responsible for only one job, like interfacing with an API or updating the view. Promises let us do that with ease, and if the behavior of one of those pieces changes, for instance, if an application is depending on an API to behave a certain way and its signature changes, then we can write different transformers to let it meet the consumer’s specifications instead of expecting the consumer to change.

Now that we’ve learned the “what”, we’re ready to learn the “how”. We’re going to spend the next post creating and combining promises, and after that, we’re going to step through a few real-life examples.

"index-pl.html"