At my office, we’re a coffee-script shop. Of late, though, more and more of the cool features of coffee-script seem to be getting ported to Ecmascript 2015, and with node.js now supporting many of these features, I decided it was time to try out a small project in ES2015. The TL;DR here is that Babel is a nice way to access fancy ES2015 features, even ones that aren’t in the latest node.js, but Babel comes with a significant compile-time overhead.

There are a lot of cool Promise libraries out there like bluebird and q, but all of these libraries make you use their implementation of Promise, and most of them are kind of on the big side (bluebird clocks in at almost 166k, unminified.) It would be nice to have a small library of async-like control flow functions, and maybe a few other handy utility functions, and one that worked with any Promise implementation that follows the ES2015 standard.

So I started out writing a small library - I implemented delay, timeout, and defer functions, and wrote a unit test for delay. Here’s the ES2015 version of the unit test:

var chai = require('chai');
chai.use(require('chai-as-promised'));
var {expect} = chai;
var promiseTools = require('../src');

describe('delay', () => {
    it('should wait the specified delay and then resolve', () => {
        expect(promiseTools.delay(10)).to.be.fulfilled
    });
});

Aside from an excess of braces and brackets, this looks pretty close to what I’d write in coffee-script:

chai = require 'chai'
chai.use require('chai-as-promised')
{expect} = chai
promiseTools = require('../src/index.coffee')

describe 'delay', ->
    it 'should wait the specified delay and then resolve', ->
        expect(promiseTools.delay(10)).to.be.fulfilled

But, trying to run this in node, we immediately run into a small problem. I’ve used a destructuring assignment here and, at least as of node v4.2.1, node doesn’t support this (not without enabling experimental features.) This isn’t a problem, though - since this is a library, we’re going to want this to run in older versions of node, and even on the browser. This will need a Promise polyfill of course, but that’s not a problem. The obvious choice here is to use something like Babel to transpile our code from ES2015 down to ES5. So let’s set up Babel to run our tests:

npm install --save-dev babel-core babel-cli babel-preset-es2015
echo '{"presets": ["es2015"]}' > .babelrc
time ./node_modules/.bin/mocha --compilers js:babel-core/register --reporter spec

Whoa. This takes a long time. Over 6 seconds on my macbook, and we’re only talking about two source files here. If we bring in isparta (the Babel equivalent of the istanbul code coverage tool) this jumps to a rather leisurely 26 seconds. If I rewrite all this in coffee-script, my test cases take 0.5 seconds to run, and even bringing in coffee-coverage, with full HTML reports from istanbul, only brings this to 6 seconds (and generating JSON coverage without generating reports is only a little over a second.)

One solution here is to remove babel from the equation, at least for running tests in development (we can get our build server to run tests on the Babel generated files.) Removing the destructuring assignment, and running the tests with just node brings this down to a cool 0.4 seconds.