Improve Performance with Web Workers
Erxk
Posted on June 19, 2019
Using the Angular 8 CLI
Introduction
This article will illustrate how to improve start-up performance using Web Workers. Our example will be an Angular 8 application. Using the Angular 8 CLI simplifies getting started with Web Workers. However, using Web Workers is not specific to Angular and most of these concepts can be utilized in any Javascript or Typescript application.
We will cover
- Measuring Performance in Lighthouse
- Getting Started with Web Workers in Angular 8 [1]
- Measuring Performance with Web Workers
- Web Worker Limitations and Pitfalls
Measuring Performance with Lighthouse
First, we need to get a baseline measurement to gauge how performant our application is on start-up without a Web Worker. Note, we are running our Angular application in production mode — This affects start-up performance.
In the Google Chrome Developer Tools đź› , using Lighthouse, we can measure the performance of our webpage on startup [2]. I have added a long-running task/computation to the startup of our application (for-loop building a massive string).
When we execute a long-running task on the Main thread, our application appears to be locked up. This is because the Main thread is blocked by all of the computations in our long-running task. Thus, the user cannot interact with our application until the process has finished.
By clicking “View Trace”, we can see a visualization of CPU time on start-up. In our example, the majority of the time spent was evaluating/running our script/task. We can also verify in the trace that our code is running in the Main thread.
Getting Started with Web Workers
The Angular 8 CLI has simplified getting started with Web Workers. To create a Web Worker, we will simply run the Angular 8 web-worker schematic.
The name and location of the worker are mostly arbitrary. A caveat, if your Web Worker has the same name and folder as your component, Angular will automatically add the following code to your component for you. If not, add this code where you want to use the worker.
The only other code generated is the actual worker itself. This is where we will move our long-running computations to.
Main thread → Web Worker → Main thread
When our Web Worker invokes worker.postMessage('hello') the content inside our 'message' event listener will execute inside the Worker. Once our task has completed, postMessage(response) will be called from the Web Worker and worker.onmessage(data)=> {} will execute inside our component back in the Main thread.
Web Worker Performance
Once we move our long-running task to the Web Worker inside of addEventListener('message', (data)=> { // Here }); we will be ready to test our performance again. Note, we will cover various limitations when executing code in a Web Worker later. For now, we simply move our code from the component to the Web Worker.
We can see that the performance of our application on start-up has significantly improved. This is because the Main thread only takes 1.8s before its finished evaluating our Javascript and rendering our components on the screen.
When our long-running task was in the Main thread, we had to wait the additional time to complete the long-running task before the application became interactive.
The application stays interactive the entire time our script/task is running, as it is not on the Main thread. Performing a “View Trace” we can verify that our script/task is running in an instance of our Worker and the Main thread is sitting idle.
Web Worker Limitations & Pitfalls
Cannot Pass Functions to Web Workers
Functions and methods cannot be passed to Web Workers. When an object is passed to a Web Worker, all of its methods are removed. If a function is passed to a web worker, the following exception will occur.
worker.postMessage(() => {return 'hello'});
Working with DOM & window
Web Workers run in a different global context than window. DOM manipulation is not allowed, and some methods and properties of the window are not available in web workers. [3]
Running very large processes
In general, there is not a significant difference in time to complete a task when running in Main or in a Web Worker. If you run very large processes inside of a Web Worker, there is a point where the Web Worker becomes significantly slower than the Main thread.
In our example, if we increase the number of iterations significantly, we can see the difference in performance changes dramatically.
Summary
- Measure the performance of an application on start-up using Lighthouse in Google Chrome Developer Tools.
- Long-running tasks/computations in the Main thread will cause the UI to lock and become unresponsive
- Delegate long-running tasks to Web Workers to improve performance
- Angular 8 CLI simplifies getting started with Web Workers
- Be aware of limitations and pitfalls when working with Web Workers
References
[1] https://angular.io/guide/web-worker
[2] https://developers.google.com/web/tools/lighthouse/
[3] https://developer.mozilla.org/en-US/docs/Web/API/Web_Workers_API/Using_web_workers
Posted on June 19, 2019
Join Our Newsletter. No Spam, Only the good stuff.
Sign up to receive the latest update from our blog.