Promises, Promises Pt. 2
Creating and Listening in ES6

In a previous post, I introduced the idea of promises and the purpose they serve, while also delivering an ode to nachos, a paean on a pan, as it were. This time, I actually want to get to the nitty gritty, and demonstrate how to create, transform, and combine promises in the new ES6 syntax. I’ll follow up with the same article in the syntax more common to Angular.JS, since that’s where most of my work has been.

Making Promises

So, let’s make a simple promise, one that resolves with the numeric value three. What does that look like?

var promise = Promise.resolve(3);

So now we have a promise that resolves with the value 3. This is very exciting. What about rejecting? Can we reject with an error?

var badPromise = Promise.reject("Boo!");

Welp, that was fun, I guess. How about this: can we create a promise that takes a random int from 0 to 3, and if it’s equal to 0, reject, and if not, return the val? Well, that’s pretty easy. The Promise constructor just needs a function that will take a resolve and reject function as parameters, then act accordingly.

var luckyPromise = new Promise(function(resolve, reject){
	var val = Math.floor(Math.random() * 4);
	if(val === 0){
		reject("Zero!");
	} else {
		resolve(val);
	}
});

Because the truth is that stuff happens. The reason that the promises have rejected states is that sometimes stuff goes wrong, and you have to adjust. Here’s another way to get the exact same effect:

var luckyPromise = new Promise(function(resolve, reject){
	var val = Math.floor(Math.random() * 4);
	if(val === 0) 
		throw new Error("Zero!");
	
	resolve(val);
});

All right, but one of the big points that I mentioned was asynchrony, and so far, these promises have all been on the same process. Let’s use setTimeout, and rewrite the last promise so that it will do all this process after two seconds.

var delayedLuckyPromise = new Promise(function(resolve, reject){
	setTimeout(function(){
		var val = Math.floor(Math.random() * 4);
		if(val === 0){
			reject("Zero!");
		} else {
			resolve(val);
		}
	}, 2000);
});

You’ll notice that I switched out of the “throw an error” model above. It’s better to be clearer, I suppose. But I really like that syntax, and I think I want to keep it, so I’m going to write a utility function.

I want to make a function that I’ll call delayer, which will take in a function and a delay in milliseconds, then return a new promise. That promise should:

  • Wait the specified delay, then evaluate the function.
  • If the function throws an error, the promise should reject with the error.
  • If it returns a value successfully, it should resolve with that value.
function delayer(fn, delay){
	// Return a promise
	return new Promise(function(resolve, reject){
		//Set a timeout
		window.setTimeout(function(){
			//Try to perform the function
			try {
				// Get the value of the function, and resolve with it.
				var val = fn();
				resolve(val);
			} catch(e){
				// If there's an error, reject with it.
				reject(e);
			}
		}, delay || 0)
	});
}

var newDelayedLuckyPromise = delayer(function(){
	var val = Math.floor(Math.random() * 4);
	if(val === 0) throw new Error("Zero!");
	return val;
}, 2000);

And boom! We have written a utility function to return promises! As you can imagine, you could write utility functions like this to do all sorts of things, such as:

  • Create a Vanilla.js, cross-platform AJAX library!
  • Listen to keyboard events and run an easter egg if the user types a certain key-combo (like “JAVASCRIPT”)
  • Access LocalStorage or cookies asynchronously

This is very low-level, of course, but what about jQuery? After all, jQuery is by far the most commonly used library if you want to make something quick with a little AJAX, which is ground zero for promises. You’ll be glad to know that jQuery.ajax returns a jqXHR, which can be treated like a promise.

var good_jqXHR = $.ajax("/good_file.json");
var bad_jqXHR = $.ajax("/missing_file.json");

There may be a tiny bit of difference betweent jQuery promises and the new official versions of promises, so if jQuery promises don’t act like you expected, there’s something to put on your suspect list.

Well, now we’ve created them, how do we respond?

Listening to Promises

We’ve got these strange little objects that are creating these events with values. How do we listen to them? How do we actually do something with those values? Well, that’s where the then and catch functions come in.

The then function takes in up to two functions as arguments: the success callback, and maybe the failure callback. The catch function only allows you to attach a failure callback. You can do whatever combination you want, but in a production setting, for most Promise specifications, there’s a good chance that you should be preparing for errors as well as success. You don’t want to annoy your users. Let’s take a look:

// Theoretical tweet promise that returns an array of tweets or errors out with an informative message.
var newTweetsPromise = getNewTweets();

// Theoretical success handler
function tweetSuccessHandler(tweets){
	// Adds new tweets to the DOM
}

// Theoretical failure handler
function tweetFailureHandler(error){
	// Launch Error modal with error message
}

// Double Then syntax
newTweetsPromise
	.then(tweetSuccessHandler, tweetFailureHandler);

// Then-Catch syntax
newTweetsPromise
	.then(tweetSuccessHandler)
	.catch(tweetFailureHandler);

One super-important thing to know about then and catch is this: if you attach them after the promise resolves or rejects, they will still fire! That’s right! With most situations regarding events, if you attach an event handler after the event fires, you’ve “shut the barn door after the horse has already run off.” You won’t catch the event that’s already gone. With promises, you can take a resolved or rejected promise and capture its value as many times as you want. Isn’t that fantastic?! You don’t have to check its state first. In fact, come to think of it, I’ve been coding with all sorts of promises for a few years now in about 4 different frameworks, and I’m pretty sure that I have never checked whether a promise is finished or not. How cool is that?

All right, we’ve created promises, and we’ve responded to promises. Everything we’ve done is asynchronous, but still very imperative; that is, we’re making the request and responding to it. But one of the strengths of promises is that we can use a few tools to transform them and combine them in a way that’s more like functional programming, and that’s what we’ll get up to in the next post.

"index-pl.html"