Oleksii Vasyliev, Railsware
Brought to you by Alexey Vasiliev, Railsware
Web Workers makes it possible to run a script operation in a background thread separate from the main execution thread of a web application. The advantage of this is that laborious processing can be performed in a separate thread, allowing the main (usually the UI) thread to run without being blocked/slowed down
Workers run in a different global context than the current "window".
The following Web APIs are available to workers
Barcode Detection API, Broadcast Channel API, Cache API, Channel Messaging API,Console API, Web Crypto API (Crypto), CustomEvent, Data Store (Firefox only), DOMRequest and DOMCursor, Encoding API (TextEncoder, TextDecoder, etc.), Fetch API, FileReader, FileReaderSync (only works in workers!), FormData, ImageData, IndexedDB, Network Information API, Notifications API, Performance API (including: Performance, PerformanceEntry, PerformanceMeasure, PerformanceMark, PerformanceObserver, PerformanceResourceTiming), Promise, Server-sent events, ServiceWorkerRegistration, URL API (e.g. URL), WebGL with OffscreenCanvas (enabled behind a feature preference setting gfx.offscreencanvas.enabled), WebSocket, XMLHttpRequest.
Workers do NOT have access to:
var worker = new Worker('worker.js');
worker.addEventListener('message', ({data}) => {
console.log('Worker said: ', data);
}, false);
worker.postMessage('Hello World'); // Send data to our worker.
self.addEventListener('message', (e) => {
self.postMessage(e.data);
}, false);
Most browsers implement the structured cloning algorithm, which allows you to pass more complex types in/out of Workers such as File, Blob, ArrayBuffer, and JSON objects. However, when passing these types of data using postMessage(), a copy is still made. Therefore, if you're passing a large 50MB file (for example), there's a noticeable overhead in getting that file between the worker and the main thread.
With Transferable Objects, data is transferred from one context to another. It is zero-copy, which vastly improves the performance of sending data to a Worker. Think of it as pass-by-reference if you're from the C/C++ world. However, unlike pass-by-reference, the 'version' from the calling context is no longer available once transferred to the new context. For example, when transferring an ArrayBuffer from your main app to Worker, the original ArrayBuffer is cleared and no longer usable. Its contents are (quiet literally) transferred to the Worker context.
To use transferrable objects, use a slightly different signature of postMessage():
worker.postMessage(arrayBuffer, [arrayBuffer]);
window.postMessage(arrayBuffer, targetOrigin, [arrayBuffer]);
The worker case, the first argument is the data and the second is the list of items that should be transferred. The first argument doesn't have to be an ArrayBuffer by the way. For example, it can be a JSON object:
worker.postMessage({data: int8View, moreData: anotherBuffer},
[int8View.buffer, anotherBuffer]);
You can load external script files or libraries into a worker with the importScripts() function
importScripts('script1.js');
importScripts('script2.js');
importScripts('script1.js', 'script2.js');
Workers have the ability to spawn child workers. However, subworkers come with a few caveats:
var blob = new Blob([
"self.addEventListener('message', (e) => { self.postMessage(e.data); }, false);"]);
// Obtain a blob URL reference to our worker 'file'.
var blobURL = window.URL.createObjectURL(blob);
var worker = new Worker(blobURL);
worker.addEventListener('message', ({data}) => {
console.log('Worker said: ', data);
}, false);
worker.postMessage(); // Start the worker.
A shared worker is accessible by multiple scripts — even if they are being accessed by different windows, iframes or even workers
var myWorker = new SharedWorker('worker.js');
worker.addEventListener('message', ({data}) => {
console.log('Worker said: ', data);
}, false);
worker.postMessage(); // Start the worker.
// Event handler called when a tab tries to connect to this worker.
self.addEventListener('connect', (e) => {
// Get the MessagePort from the event. This will be the
// communication channel between SharedWorker and the Tab
const port = e.ports[0]
port.addEventListener('message', (evt) => handleWorkerMessages({event: evt}))
// Required when using addEventListener.
// Otherwise called implicitly by onmessage setter.
port.start()
})
Service workers essentially act as proxy servers that sit between web applications, the browser, and the network (when available)
if ('serviceWorker' in navigator) {
navigator.serviceWorker.register('/sw.js').then(function(registration) {
// Registration was successful
console.log('ServiceWorker registration successful with scope: ',
registration.scope);
}, function(err) {
// registration failed :(
console.log('ServiceWorker registration failed: ', err);
});
}