Wednesday, September 24, 2014

Dynamic Worker Threads In JavaScript

Patrick knows what to do

Recently, I've been doing a lot of performance work on a real-time data visualization app at Netflix. This app in particular is getting event streams (via web sockets) from the Netflix cloud. These event streams are aggregated in various ways so I don't get a firehose of events via some brilliant work by my colleagues, but nonetheless, it's a lot for single-threaded browser app to process, especially when that browser app also has a rich, interactive UI.

This means that what I really need to do is spin up another thread to separate the work load between UI related concerns and I/O or data map/reduce concerns. The idea is to free the UI thread up as much as possible, so it doesn't "jank" when I'm doing things like parsing JSON that arrived on the web socket. Generally, a snapshot of this concept looks a *little* like this:

Behold: An overly simplified, off-center, and crappy diagram of two event loop threads mating in their natural habitat!

There are other things I'm doing in this area, for example using RxJS to buffer incoming pushes on the socket, setting up back-pressure handling, etc. But I'm not going to get into that stuff in this post. What I want to talk about here is web workers. Just the basics really.

How can you make a multi-threaded JavaScript app?

The first question someone might have is simple: "You're gonna to what?"

It's actually easier that it sounds. You just create a new Worker:

Then your script file will be something like this:

Pretty darn simple. If your browser supports it, you just create a new worker, and point it at a script you'd like to run in that thread. Both the worker object in the window, and the worker script's global scope have access to an `onmessage` event, and a `postMessage` method. The `postMessage` method will send a message event to the other side, which is handled by the `onmessage` event. Really, really basic.

How can I build one of these dynamically?

So then there's problem developers face for a variety of reasons. Maybe you need to create some worker on the fly because of variable conditions in your app. Maybe your JavaScript build environment makes unit testing separate, standalone scripts a PITA. Who knows? So what do you do? Well, Blob URLs to the rescue:

Easy peasy.  You can take any string containing JavaScript and use it to create a worker. Just make sure you also teardown the URL when you're done:

"But dynamic workers seem less testable, Ben, you dork."

Yeah. The above example is probably going to be even harder to test, I suppose. You have this string, that's a script. Evaling that string and then testing does not at all sound appealing. At least to me. So there are a few other approaches. Here are two that I like the best:

1. Behavioral testing of the live worker

Actually spin up the worker in your test, send messages to it and check the responses. This could get sloppy because maybe your worker doesn't always send responses to things you post to it. Also async gets nutty in tests sometimes.

2. Build your worker from functions and test those

For his approach, we create functions on some testable object or in some testable space we already have access to in our tests. So in Angular it might be a service or controller, and in Ember it could be any Ember Object (Controller, a Route, Model, etc). The idea is we just add a function to it, that we're going to use to create the worker.

The idea is that you can create a function that takes a global context as an argument. The reason I'm choosing to pass it as an argument, and not as `this` via binding is simply because a lot of frameworks (JQuery and Ember for example) make a lot of use of the `this` keyword, and I want to limit confusion about what this is. It's a global scope that doesn't belong to the current class.

Once you do that, you'll be able to call the worker function, or even bits and pieces of it, depending on how you use this trick and unit test those parts along with the rest of your framework code.

For my example, I'll use a plain JS class:

To use this Muppet class it might look a little like this:

And now some tests (in a Pseudo-Jasmine style, but this will work in any testing framework):

As a side note, I've "DRY-ed" up the tests above for the terseness of this blog entry. I'm actually against DRY tests, but that's a whole other blog post I suppose. This StackOverflow answer sums up some of my feelings on that, I suppose.

Also, one goofy thing about Jasmine is the need to reset spies after they've been called once, so the code above might not exactly just "work" if you copy-pasta, because I've taken out the bits that will reset the spy. Again, for terseness.

Additional things to know about Workers

  1. importScripts() - is a globally available function for loading other scripts into your worker. For example you could load RxJS in like so: `importScripts('', '');`.  Be aware if you're using Blob URLs you might run into issue with relative URLs in import scripts.
  2. Workers have no document!  This means any script you might want to load that requires the DOM (Ember, Angular, or D3 for example) will not load properly in your worker.
  3. Workers, therefore have no DOM-related stuffs. You won't be able to build HTML via DOM objects and send them back to your UI thread.
  4. You can only "postMessage" objects that can be "structured cloned": This is complicated to explain, I guess, but to keep it simple: you can send POJOs back and forth or simple struct-type class objects.
  5. Workers can be a PITA to debug. For example: breakpoints aren't always hit, and console.log behaves differently. Therefor, I think it's a *really really* good idea to unit test your worker directly.

Go forth and enthreadify all the things!

I kid, I kid. You probably shouldn't use Workers unless you have to. But in the end, there are a LOT of ways to skin this cat. Now with the tools to convert any string containing JavaScript into a URL that can be spun up in a different thread, developers should be able to use threading in a variety of interesting, and testable(!!) ways.

There are still a lot of other things to talk about: MessageChannels, for example. But that, again would be a different blog entry. I just want to mention them so anyone reading this hopefully does some Googling.

Thursday, September 18, 2014

Working with SVG in Angular

I don't have a good picture, so here's Igor with an SVG mustache.

Recently I posted about how to handle SVG in Ember. Angular has some issues itself with SVG, and it's worth discussing here, partially because I recently contributed a lot to the effort to get SVG working in Angular.

For the most part, Angular won't have too many problems with SVG, as long as you're using attribute-style directives, and you're not creating new directives. The issues Angular has with SVG will become more apparent when you're dealing with custom directives, particularly those using custom-elements, transclusion or template strings.

Angular 1.3.0-beta.19 has first class SVG support

The short story is, if you want to use SVG with Angular, it's a good idea to use 1.3.0 beta 19. I submitted and issue, then did a late-night hack-session with Igor Minar get SVG working in Angular. The approach was basically to track namespace while Angular was traversing the DOM looking for directives during $compile. The code in that area is a little tangled because of how it uses closures to pass around transclusions, found directives, and other important pieces of information. After we had it mostly working, Tobias Bosch and Igor (and probably others on the team) went back through the code and cleaned it up, fixing a few issues we didn't find the first time.

So what about 1.2?

The current stable version is 1.2. It is my personal opinion that if you want SVG support, at this point you're better off upgrading to 1.3.0-beta.19. This is because the hacks you'd have to perform to get SVG 100% working with Angular 1.2 would be much more than the work it would take to upgrade your app, and any subsequent beta-related code breakage.

Angular's issues with SVG

The problems with SVG in Angular are the same problems found in almost any framework, with one interesting exception, cloning custom elements.

1. Parsing HTML

This happened in cases where template strings were being converted into DOM elements so they could be compiled.  Parsing HTML strings was done with a wrap map that didn't support SVG elements. I went through what a wrap map is in my previous post about Ember and SVG, but as a refresher: Basically, you create a div object, then you set it's innerHTML to your HTML string, which is sandwiched between the proper tags. So if you had a string like '<td>whatever</td>', you'd create an HTMLDivElement with document.createElement, then you'd concat '<table><tbody><tr>', your string and the closing tags, then assign it to div.innerHTML. After than you'd grab the nodes from the appropriate place in the DOM tree of div.

2. Cloning custom elements

The reason this one is interesting is because Angular leverages the existing DOM as it's view, where other frameworks process templates and output DOM. This means that Angular can do efficient things like clone existing elements to create new elements. The also means that Angular is going to try to clone everything, even custom tags, exactly as they exist in the DOM. SVG 1 doesn't know what to do with custom tags. SVG 2 will treat them like <g> tags, but no browser supports SVG 2 yet, AFAIK.  So what happens when you clone <my-tag><circle /></my-tag>? It clones successfully, but <my-tag> has no layout, so everything underneath it will not render. This means that all directives that are custom elements for SVG need to have `replace:true`. This is something that is true even in Angular 1.3.0 beta 19 and higher.

3. SVG validation error messages

Since Angular is using real DOM to set up it's views, that means you're liable to run into situations where you're trying to bind to an attribute in svg like `<rect x="{{x}}"/>` The problem here is you'll get error messages all over your console saying that {{x}} is an invalid value for rect (or something like that). Fortunately, there is already a fix in angular for this in the form of the `ng-attr-` binding style. That means you can do `<rect ng-attr-x="{{x}}"/>` and everything will will be fine.

Custom SVG elements in Angular 1.3.0-beta.19 and higher

There is a new `templateNamespace` property in the directive configuration object that you'll need to set, and you'll want to set `replace` to true. But other than that, it's now pretty straight forward.

Here's an example:

Wednesday, September 17, 2014

Working with SVG in Ember

(Tomster constructiony logo thing... unsure what artist to credit)

At the time of this post, working with SVG in Ember can be a PITA to say the least. At Netflix, I'm working on an Ember application that is at least 90% SVG, and I've hit a lot, if not all, of the pain points you might have before finding this post. Hopefully someone else finds this helpful.
The issues faced in Ember with SVG are the same issues faced by most frameworks. In fact, as of right now (now meaning while I'm typing this), there are no stable versions of JavaScript "frameworks" that fully support SVG properly. Angular, Ember, React and Polymer all have some pain around handling SVG. Some of them attempt to handle it, React for example, but there are certain cases where it will still break.

First Problem - Partials getting parsed incorrectly

This happens when the root of a template is an SVG element. Ember, like JQuery, React, and in some cases Angular, use a "wrap map" technique to parse HTML strings.  This essentially creates a div tag, puts your string wrapped in some other tags into the div's innerHTML, then pulls out the child nodes you required. That's a simplified version of the technique, and I can go over that in another post. The "map" comes in when trying to determine what to "wrap" the string you provided with. For example wrapping a <td> with <table><tbody><tr>.

The problem with this technique is that SVG elements, being in a different namespace than HTML elements, will not be created properly if they're not inside of an <svg> tag. And all 50 or so SVG tags are simply not in the wrap map.

Second Problem - CSS Classes not being applied properly

Under the hood Ember uses JQuery to add and remove classes from elements. The problem here is that JQuery doesn't support SVG. I could go on a rant here about how I don't like JQuery, but meh.  The reason JQuery doesn't work to add and remove classes from SVG elements is that JQuery tries to do so by setting element.className to a string. In SVG, element.className is an SVGAnimatedString, and you must set baseVal to the string instead. But better still, if you're in IE8 and newer the "modern browsers" JQuery 2.x and higher purports to target, you can just use classList to add and remove classes. Alternatively, setting class via setAttribute('class', 'whatever') will also work with SVG.

The Solution (for now): 1.7.0 beta.5 with a script

Thanks to Stefan Penner, I was able to push through a PR to Ember that exposes the wrap map used in Handlebars via Ember._metamorphWrapMap. This allowed me to create a patch script that updates the wrap map with the appropriate elements for SVG. That same patch also updates JQuery's addClass and removeClass to use classList internally, which might break for really old browsers. (Also, as of right now I'm not supporting passing functions to those methods but feel free to submit a PR if you need that feature).

  1. ember 1.7.0-beta.5 has the wrap map exposure. (via bower: bower install ember#1.7.0-beta.5)
  2. ember-handlebars-svg is a script I made to update the wrap map and fix class manipulation. (via bower: bower install ember-handlebars-svg)

After that everything should "just work". No other JQuery patches or anything are required unless you're trying to use JQuery for animation or something, in which case, I'd recommend ditching that and using D3. There are still a few places you could have a problem, specifically if you try to use an svg <a> tag as a root element to a template, and that's unavoidable because of the wrap map approach, but other than that, you *should* be okay. (should, haha).

The FUTURE!!!!

In short, the future of SVG in Ember looks 100% awesome. And the answer is "HTMLBars".

I've talked about this issue at length with Stefan Penner, Kris Seldan, Erik Bryn and Matthew Beale and how it's going to be solved in the future of Ember. Currently SVG in 1.8.0 with metal-views is broken again. But not to worry: HTMLBars and metal-views will allow for a more complete solution to this problem that will use namespace tracking instead of clumsy wrap maps to provide functionality to properly create elements in each namespace. This means it will work 100% of the time, where a wrap map will still have some issues (for example both HTML and SVG have an <a> tag).

Stay up to date

When it comes to the status of HTMLBars and SVG support, I'd recommend following these folks on Twitter:

Kris Seldan
Erik Bryn
Matthew Beale
Stefan Penner
and of course Tom Dale

Generally, when there's a new release or a big, new feature added to Ember, Tom tweets it immediately. Stef's mostly good for information regarding Ember-CLI, but the others are all involved in the development of HTMLBars and SVG support, so they're important to follow. Also, when HTMLBars is released with SVG support, I'm liable to sing it from the rooftops. ;)