A Promising Start - Embracing Speed and Elegance with Zousan Promises

July 6, 2015

In this article I describe why and how I created the world's fastest Promise implementation.

In my last post I described the key ingredients of a Javascript foundation. One of those key ingredients was that any good foundation should embrace asynchronicity, and that asynchronicity should be based upon Promises. Very few Javascript frameworks adhere to this requirement, which is one big reason why we are embarking on this journey to build an ideal Javascript foundation here on this blog.

If you follow Javascript discussions much, you have probably heard of Promises. If not, here is a pretty good writeup explaining them. Its a great little re-think of the oft-used pattern of using callbacks. Once you start using promises in place of callbacks in your APIs, you will experience the benefits and will want to use them in all of your asynchronous code. In fact, their value increases the more you use them exclusively - due to their chaining and grouping ability.

The real power of promises is that it takes an asynchronous operation and turns it into a first-class object

Keep in mind that the value of promises is not (just) in the improved syntax or the added clarity it brings to complex asynchronous code. To focus only on that benefit is missing the point of promises – or at least severely underselling it. The real power is that it takes an asynchronous operation and turns it into a first-class object: meaning you take that operation and can:

  • give it a name
  • return it from a function
  • pass it around
  • store it for later
  • push it onto an array
  • and all the other fun stuff you can do with any variable in Javascript

This ability opens up a world of compositional possibilities. Take special care in understanding this – it is the entire operation, including the arguments passed and the state of the operation that is encompassed in this object.

They are not a brand new concept to Javascript. Version 1.0 of the spec was published in 2012. They are a part of the ECMAScript 2015 (ES6) spec which will eventually be supported in all browsers.

In fact promises are included in most current browsers already and you could nearly go ahead and use them directly right now, if it weren't for one particular browser. Guess which browser that is.

It turns out that currently only about 63% of browser traffic can take advantage of Promises. So we need a shim.

Well, that surely won't be a problem – we have an accepted standard, so plenty of people have written compliant libraries we can use. In fact, jQuery has one built in!  No problem right? 

Well, it turns out that jQuery uses a different API than the spec defines and is significantly less useful. Luckily there is a good list of conformant implementations. So, just include one of these puppies in your builds and you're golden, right?

Well, thats true. But for resource and performance conscious developers (thats you, right?) the excitement quickly turns to horror as you see the byte weight of these things.

The Bluebird Promise implementation, which is considered the fastest and most comprehensive solution available, is a whopping 72,436 bytes after minification.

There are smaller options available, but their performance is abysmal. In fact, in the early days of promises, all the available implementations were pretty slow, leading many to believe that promises, by their nature, were far too slow to replace callbacks in performance-sensitive code.

So now we know that promises can be small, and promise shims can be performant, but what about small and performant? Well, I had no luck finding any implementations that satisfied that tiny wish list. So I wrote my own implementation that did.

Introducing Zousan

Zousan, which means "elephant" in Japanese child-speak, is a promise shim that is fully conformant to the Promise/A+ specification. It is about 2k minified (less than 1k gzipped) and in performance tests its even faster than Bluebird:

Zousan performance graphInterestingly, it is much faster than the native Promise implementation (red bar) in modern browsers. So it is likely that even once ECMAScript2015 (ES6) fully rolls out to browsers, you will still benefit from using Zousan instead.

How Can Zousan Be So Darned Fast?

Zousan takes advantage of the super fast asynchronous mechanism I call soon() and blogged about here a couple months ago. In fact, while writing Zousan I found a way to squeeze a bit more performance from the soon() module - so look for a part 2 to the soon() article if you want the details.

Another technique is to eschew most functional programming patterns and instead choose imperative programming. For example, iterate through arrays with a for-loop rather than Array.forEach:

var myArray = [];

// populate array with a bunch of data
// ...
//

// This technique to process items in your array
for(var x=0;x<myArray.length;x++)
	processItem(myArray[x])

// is faster than this technique
myArray.forEach(function(item) {
		processItem(item);
	})

This is especially true for small arrays, since the cost of creating that anonymous function becomes proportionally large.

And finally, be lazy. Your mother may have told you this was a bad thing, but in performance and memory conscious coding, it can be a real benefit. Creating a new Zousan promise via "p = new Zousan()" incurs the absolute minimal cost possible – nearly zero beyond a simple object creation. Here is the Zousan constructor:

function Zousan(func)
{
	if(func)
	{
		// … little bit of code here …
	}
}

That's right. The constructor does nearly nothing - particularly if you pass nothing. All state variables and listener trackers are created on an as-necessary basis. Lazy, eh? But oh so fast. And in cases where a promise is created and resolved before any then() calls hook into it, no trackers are created ever.

These techniques collectively make for an extremely efficient implementation – both in terms of speed and memory usage. One that handily beats the built-in Promises of modern browsers as well as any other implementation I've tested.

Note: This degree of "detail sweating" and sacrificing of elegance for raw speed is not recommended for general programming. Donald Knuth famously said:

premature optimization is the root of all evil.

But his follow-up statement was:

Yet we should not pass up our opportunities in that critical 3%.

In higher level programming such as web development, it is probably closer to 0.3%. But as promises are to become the underpinning of all asynchronous behavior in Javascript apps, they certainly qualify as critical and deserving of full optimization.

How Can Zousan Be So Small?

Actually, there are many promise implementations smaller than Zousan. PinkySwear, for example, is just 842 bytes minified. But this is achieved by making no efforts in performance at all, resulting in painfully slow code. (Its performance bar is so small in the chart above, it all but disappears).

But among the faster solutions, Zousan is much smaller in size than others. Part of this may be due to my efforts to keep it small. Other libraries do not appear to have that criteria in their objectives at all. But the biggest contributor to libraries like Bluebird being so large is that they pack in a bunch of additional functionality.

With Zousan I decided to provide a spec compliant module with minimal extras (I did include an all utility function as it is nearly essential). The other promise-based patterns are generally easy enough to write for those projects that need them. And eventually I'll likely create an extra module that can be included (or copy-pasted from) to make it even easier. 

So What To Do Now?

Include Zousan in all your web applications and start migrating to promises right away. Its so tiny, there is virtually no cost to include it in your apps, and you can begin taking advantage of the power of promises immediately. Write any new asynchronous functions to return promises and as time permits, convert any existing callback-based code into promise-based code.

A word of warning: many developers (possibly most?) find it difficult to wrap their heads around promises at first. Promises invert the responsibility (or at least appear to invert it) within asynchronous calls and this can take some time to get used to. You may be tempted to return to the apparent "safety" of callbacks - something you felt you understood and had more control over. But try to resist that temptation and stick with promises until you have your "ah hah!" moment. You will be happy you did. I prom… er, I assure you.

 


If you or your organization could use a somewhat fanatical developer on your current or next project, lets talk. I am passionate about the user experience, easy to work with, and I get stuff done. Lets build something great!


A Promising Start - Embracing Speed and Elegance with Zousan Promises Tweet This!
Glenn is the founder of bluejava K.K., and has been providing consulting services and developing web/mobile applications for over 15 years. Glenn lives with his wife and two children in Fujisawa, Japan.
Comments
(Comments currently disabled)