Introducing Web Workers
Introducing web workers
This section introduces the concept of HTML5 web workers. If you're already familiar with web workers, you're free to skip this section. If you're not already familiar with how web workers are implemented, or just want to brush up, this section is for you!
threading
Threading is a basic programming concept that allows developers to execute code on a separate process. Threading is extremely helpful when working with operations that either require additional processing power, or may take a long amount of time.
Applications typically start with a single thread that is used to execute code and update the user interface. If an operation is long running, and it executes on that thread, the user interface isn't able to be updated, and thus freezes. This provides a bad experience for the user. By using separate threads, you can execute your long running code elsewhere, allowing the user interface to still be responsive to the user.
As mentioned above, threading is an extremly powerful tool. Unfortunately, this tool can easily be mismanaged or abused, leading to degraded performance or potential security risks. This poses a challenge when working with web applications, in which users execute code (JavaScript) without knowing the developer of that code. Allowing threading in a browser could create an undesirable experience for the user. As a result, browsers don't allow JavaScript to use threads.
This is where web workers come into play.
Creating a web worker
Web workers
A web worker is made up of two components, the parent or calling script, and the worker or executing script. The worker runs in an environment similar to a separate thread, and does not have direct access to the calling environment or the UI. Web workers use a messaging system to pass information to and from the worker.
Creating the worker script
To create a web worker, you create a separate JavaScript file. This file will contain the code that will execute in the worker environment. The code in the file will execute immediately when the worker object is created from the calling script. As a result, if you wish to defer execution in a worker, the code will need to be contained inside of a function.
self
The web worker environment provdes an object named
self
, which represents the worker. self
has one function and one event.
The worker provides a function named
postMessage
that is used to send data to the calling environment. postMessage
accepts most data types, including JavaScript objects.// send a signal back to the calling script
self.postMessage('hello from the worker!');
The worker offers one event,
message
. message
is raised when the calling script has sent a message to the worker. message
is raised when the calling environment calls postMessage
, and thus almost any type of object can be received. The data passed into postMessage
is available by using the data
property of the event object.// Receive a message from the calling environment
self.addEventListener('message', function(e) {
// the data property will contain the data passed from the calling script
});
Calling a web worker
Calling a web worker
To call a web worker, you create an instance of the HTML5
Worker
object. Because web workers are a relatively new development, it is a best practice to first check to see if the browser supports web workers. This can be done by testing if Worker
is equal to null, meaning it doesn't exist. If Worker
is null, you know the browser doesn't support web workers.// Test if the browser supports web workers
if(Worker == null) {
alert('You need to upgrade your browser!');
} else {
// do your work here
}
Creating an instance of the Worker object
The constructor for
Worker
accepts one parameter, the location of the script it will load into the worker space. Remember, the script will execute immediately, so unless you're certain it's been built to allow you to start it manually, don't create the instance until the last possible momen.var worker = new Worker('script-location.js');
Similar to what we've already seen, the worker object offers a
postMessage
method to send data to the worker space, and an event message
that is raised when the worker sends a message back to the calling page. The parameter you pass to postMessage
is retrieved by using the data
property of the event object in the message
event handler.// Register event handler
worker.addEventListener('message', function(e) {
$('#output').append('<li>' + e.data + '</li>');
});
worker.postMessage('Started!');
Web worker design practices
Designing your web workers
Creating a web worker that accepts status messages
As you may have noticed, the web worker doesn't provide a built-in structure for handling common events, such as start and finish. However, the worker's simple messaging system allows you to easily build your workers to perform the operations you need, and add your own system for managing start and stop events.
Quite frequently, you will want to delay execution of the worker script until the caller sends a signal to start. Remember, when your worker script is loaded, the script is run immediately. You can change this behavior by adding a simple check to the worker for a start message.
Because JavaScript is weekly typed, the
data
property of the event object passed by the workers doesn't need to be set in advance. You could set it to your status strings, such as START
and STOP
when you're sending those types of messages, and use a JavaScript object in data
when you need to send other payloads.
The script below is one simple implementation of the behavior described, using simple strings for event management. You can use other objects as you see fit, depending on the complexity of your needs.
// worker.js
self.addEventListener('message', function(e) {
if(e.data === 'START') {
// Start message received.
// Begin work
startWork();
} else if (e.data === 'STOP') {
// Stop message received.
// Perform cleanup and terminate
stopWork();
} else {
// A different message has been received
// This is data that needs to be acted upon
processData(e.data);
}
});
function startWork() {
// code to start performing work here
// send a message to the calling page
// worker has started
self.postMessage('STARTED');
}
function stopWork() {
// cleanupp code here
// stop the worker
self.postMessage('STOPPED');
self.close();
}
function processData(data) {
// perform the work on the data
self.postMessage('Processed ' + data);
}
Calling a web worker that accepts messages
One of the great advantages to having a worker that's been built to accept status messages, such as start and stop, is it makes it very easy to get everything set up, and then start the worker process when you're ready to have it run.
If you were using the worker that's been designed above, you would use it by following a couple of basic steps.
- Create an instance of
Worker
, passing in the script. - Add the event handler for the
message
event. Ensure the event handler can respond to the status messages and normal data. - When you're ready to start the worker's work, call
postMessage('START');
- When you're done, send the stop message by calling
postMessage('STOP');
// inside of HTML file
var worker = new Worker('worker.js');
worker.addEventListener('message', function(e) {
if(e.data === 'STARTED') {
// worker has been started
// sample: update the screen to display worker started
$('#output').append('<div>Worker started</div>');
} else if(e.data === 'STOPPED') {
// worker has been stopped
// cleanup work (if needed)
// sample: update the screen to display worker stopped
$('#output').append('<div>Worker stopped</div>');
} else {
// Normal message. Act upon data as needed
// Sample: display data on screen
$('#output').append('<div>' + e.data + '</div>');
}
});
// When you're ready, send the start message
worker.postMessage('START');
// Send data as needed
worker.postMessage('sample data');
// Stop worker when you're done
worker.postMessage('STOP');
No comments:
Post a Comment