Solving cross-cutting concerns in JavaScript with decorators

Isn’t the state of JavaScript development wonderful?

So I’m building this fairly robust little JavaScript workflow infrastructure. Within it, each hosted “step” can perform operations – and some of those operations may perform real persistence and need concurrency checks to ensure data integrity. So, let’s say a step has operations ab and c, where and need the concurrency check. I wanted a clean way of opting into that check, but of course I didn’t want to add noise to each underlying operation – this is a cross-cutting concern which has effectively nothing to do with the actual operation.

In ES5, we’d just do this imperatively with a wrapper function like this:


function verifyConcurrency(originalFunction) {
return function () {
return this.verifyConcurrency().then(function() {
return originalFunction.apply(this, arguments);
});
};
};
}

..and then we’d use that higher order function to wrap our various opt-in methods:

var myObject = {
  a: verifyConcurrency(a)
}

However with the lovely forthcoming ECMAScript decorator proposal, we can can apply this same pattern in a clearer manner using decorators:


function verifyConcurrency() {
return function (target, key, descriptor) {
const originalFunction = descriptor.value;
descriptor.value = async function () {
await this.verifyConcurrency();
return originalFunction.apply(this, arguments);
};
return descriptor;
};
}

And then the target function need only be decorated (adding very little noise):

class Myclass {
  @verifyConcurrency()
  async a(){
    ...
  }
}

This is very easy to set up in your project:

  • npm install babel-plugin-transform-decorators-legacy and add it to your babel pluggin configuration.
  • Assuming you are linting, npm install babel-eslint and change your lint configuration to use babel’s eslint parser (as eslint doesn’t-yet support this experimental language feature).

Now you can use the decorator syntax and enjoy the improved code clarity 🙂

Leave a comment