What are JavaScript Workers?
Web responsiveness and performance is becoming increasingly important both for user experience, and SEO. One big thing that has always hindered us in these areas is lengthy JavaScript execution. Calling scripts at the bottom of your document, and using asynchronous JavaScript helps, but certainly can’t fix all your problems.
Let’s say you have to do a lot of image processing and some other expensive computations to handle when your page loads. While you can do some of this work asynchronously, at the end of the day everything still has to be eventually handled by the same single JavaScript thread. What workers do is provide other threads to run your code on. Essentially, workers are just JavaScript files that can be run on separate threads at the same time as your main JavaScript thread.
The typical types of workers you run in to are Dedicated Workers, Shared Workers, and Service Workers. You can simply instantiate a new worker as a variable in your code, and then use each worker’s respective methods and event handlers to communicate between that worker and the main thread in your project. It’s important to keep in mind when writing the code in your worker file, that workers don’t have access to your window’s global scope, and instead have their own scope, methods, and functions that they have access to. Because of this there are some things you can’t do within a worker. For instance, you can’t manipulate the DOM from a worker, or use some window properties. To communicate between these workers and your main thread, you use postMessage(“example-data”)
to send data. An onMessage
event handler is used to handle these postMessage calls.
Dedicated workers, new Worker(“example.js”)
, are JavaScript files that are only accessible by the script that called them initially. It’s a nice way to move a specific piece of standalone logic into it’s own thread, as long as you only need access to that code from a single source.
Shared workers, new SharedWorker('example.js')
, on the other hand can be accessed from multiple different scripts, or browsing contexts. Because of this the communication between these workers and your scripts has an added step. There is a port object attached to your worker that must be called to use your postMessage()
and onMessage
functionality. While this is done implicitly for you in a dedicated service worker, you must specify it in a shared worker, as the MessageEvent interface used to communicate with workers can sometimes return an array of ports. So in short there’s just an extra step when communicating with a shared worker, but you can access that worker from any scripts, windows, or iFrames you’re using, provided they’re under the same domain.
Service workers are a little different. They are workers that are set up to intercept and handle network requests. They are often used to handle offline capabilities, but also can be used to handle caching, updating of resources, or other background server and request related operations. These are a little more complex, as they require an initial registration, local installation on initial page load which handles things like setting up local caches, then activation to make the worker available for use. While this does take a bit of setup, the advantages of being able to handle unstable connections could be a huge benefit.
If your application is suffering from heavy computations, and you need a way to do some heavy lifting without blocking your main thread, workers are likely a great solution. If you’re interested in learning more about workers, here are some great resources:
Web Workers API - MDN Web Docs
Using Web Workers - MDN Web Docs
Service Worker API - MDN Web Docs