The Windward Studio

When to Abandon Web Workers

We’re creating a word processor that runs in the browser. A web worker recalculating the layout of the document in the background seemed at first to be a no-brainer. That would have no impact on the UI and could recalculate as a background thread. This is definitely how you would code it if it was a desktop application.

So, I coded the whole thing up, with async messages between the main/UI thread and the layout worker. It all worked great.

And… we then threw that away and now do the layout in the main thread.

Severe Constraints of Web Workers

Why did we abandon web workers?

First, you cannot share memory between the two threads. You need to pass JSON objects (it’s a bit more than just JSON but close enough for this discussion) from one thread to the other. So you’ve now added in passing the parts of the document object you need from the layout thread to the main thread. In the overhead of doing this you need to have a way to specify what you want, copy that out, and pass it across.

When you pass these objects across, it’s just the data. It will have the structure of the object with all properties set. But the proto is for the class Object, not for the actual class of the object. And you don’t want to assign the correct value for proto as that dramatically slows down the JavaScript optimizer. So you have to pass each JSON object into a constructor for the correct object type to get back that object. And sometimes rebuilding the objects where they point at each other is a royal PITA.

One nice thing about how it passes an object across is it includes everything it points at. And those objects include everything they point at. And those objects… So you don’t have any pointers to unresolved objects.

One really bad thing about how it passes an object across is it includes everything it points at. And those objects include everything they point at. And those objects… So we could end up copying the entire document object across. That would kill performance & responsiveness. Your choice at this point is to either design the document objects so they are descrete page objects that do not point at each other, or build an object to copy across on each request. Both approaches have big downsides.

Second, the worker thread is not interruptible. When you post a message to it, that message is not processed until the message you are currently processing has completed. If we’re calculating the layout and there is another change such as the user typed another letter, we need to stop our work and re-start. The only way to do this is to calculate layout for 1/60th of a second, then call setTimer() to continue the layout, then return. We may then get a message telling us to re-start. So the hope of avoiding using setTimer() in the main thread – we just moved that approach, we didn’t eliminate it.

Ok all of the above is a giant pain but programming is not always easy. Sometimes you have to do complex things for the optimal solution. But we then hit a third item that killed this.

Third, the web workers added unnecessary complexity. Our layout code is fast. Really, really fast. We can lay out a 200-page document in under 2 seconds. Our code was so fast, there was no need to have it performed in background processing. Most layout is within a paragraph. Next most is from the edit location to the end of the page. Aside from global style changes, layout never has to go past a hard page break. So we can perform the layout in the main thread. We can do it without even using the setTimeout() trick because it’s so fast. And then all code uses the same document object. And if the layout code does not return until the layout is completely recalculated, then it is safe for any other code to operate on the document object as it is always consistent.

Web Workers: One Instance of Beautiful Code but a Sub-Optimal Approach

If you’re considering a web worker, keep the above in mind. It’s a different world from a worker thread in a Java or C# application. Same concept, but very severe constraints. I probably spent over a week across the different parts of this implementing the web worker communication, time slicing, etc. It was really beautiful code and did an elegant job solving the problem. But it was a sub-optimal approach and so we threw it all away.

Author: David Thielen

Dave, Windward's founder and CEO, is passionate about building superb software teams from scratch and dramatically improving the productivity of existing software teams. He's really proud that he once created a game so compelling (Enemy Nations) that a now-professional World of Warcraft player lost his job for playing it incessantly on company time.
You can read more from Dave on his personal blog, and at Huffington Post.

Links

Client Success Story

"Windward has been a godsend that has saved us about 100‐200 hours at $100 per hour of work and given us a big competitive advantage over our rivals. It's been a great product."

- John Douglas, Sandia National Labs

"Thank you very much! And if you get a chance to have a word with your development colleagues, tell them we did with Windward in about 8 hours what it took us 3-4 weeks to do with jasper reports - that's downright impressive. "

- Jon Charnas, WitchLake

"The report template design using MS Word saves 40% of report development time. Our average estimation for report development was 2 days before using Windward Reports, and it's now reduced to 1 day. This results in cost savings of $80,000 for 200 reports developed so far."