Web APIs Cheatsheet
Web Workers
Use this Web APIs reference while you build software engineering projects, review code for technical interview prep, or polish examples for a software engineer resume.
Dedicated Workers
// main.js — spawn and communicate const worker = new Worker('./worker.js', { type: 'module' }); // 'classic' default worker.postMessage({ action: 'process', data: [1, 2, 3] }); worker.addEventListener('message', e => { console.log('Result:', e.data); }); worker.addEventListener('error', e => { console.error(e.message, e.filename, e.lineno); e.preventDefault(); // suppress console error }); worker.addEventListener('messageerror', e => { // Fired when message cannot be deserialized console.error(e); }); worker.terminate(); // immediately stop worker
// worker.js — inside the worker self.addEventListener('message', e => { const { action, data } = e.data; if (action === 'process') { const result = data.map(n => n * 2); self.postMessage(result); } }); // Alternatively onmessage = e => { postMessage(e.data * 2); }; // Error inside worker propagates to main thread 'error' event
Worker Constructor Options
| Option | Values | Description |
|---|---|---|
type | 'classic' (default) 'module' | 'module' enables ES imports inside worker |
name | string | Debug name, visible in DevTools |
credentials | 'omit' 'same-origin' 'include' | For module workers |
Transferable Objects (zero-copy)
Transferring ownership moves the buffer — the original becomes unusable. Much faster than copying large data.
const buffer = new ArrayBuffer(1024 * 1024); // 1 MB // Transfer to worker (buffer is now detached in main thread) worker.postMessage({ buffer }, [buffer]); // Transfer back from worker self.postMessage({ buffer: data.buffer }, [data.buffer]); // Transferable types: // ArrayBuffer, MessagePort, ReadableStream, WritableStream, // TransformStream, ImageBitmap, OffscreenCanvas, VideoFrame
Service Workers
Act as a network proxy. Lifecycle: install → activate → fetch. Scope is based on script location.
// Register if ('serviceWorker' in navigator) { const reg = await navigator.serviceWorker.register('/sw.js', { scope: '/' }); reg.update(); // force update check reg.unregister(); // unregister reg.installing; // installing SW reg.waiting; // waiting SW (update ready) reg.active; // active SW } // Communicate with active SW navigator.serviceWorker.controller?.postMessage('ping'); navigator.serviceWorker.addEventListener('message', e => console.log(e.data));
// sw.js — Service Worker self.addEventListener('install', e => { e.waitUntil( caches.open('v1').then(c => c.addAll(['/index.html', '/app.js'])) ); self.skipWaiting(); // activate immediately, bypass waiting }); self.addEventListener('activate', e => { e.waitUntil( caches.keys().then(keys => Promise.all(keys.filter(k => k !== 'v1').map(k => caches.delete(k))) ) ); self.clients.claim(); // take control of open pages immediately }); self.addEventListener('fetch', e => { e.respondWith( caches.match(e.request).then(cached => cached ?? fetch(e.request)) ); }); // Background Sync self.addEventListener('sync', e => { if (e.tag === 'sync-messages') e.waitUntil(flushQueue()); }); // Push notifications self.addEventListener('push', e => { const data = e.data?.json(); e.waitUntil(self.registration.showNotification(data.title, data)); }); // Notify clients self.clients.matchAll().then(clients => clients.forEach(c => c.postMessage({ type: 'UPDATE' })) );
Worker Scope — Available APIs
| API | Dedicated | Shared | Service |
|---|---|---|---|
fetch | Yes | Yes | Yes |
XMLHttpRequest | Yes | Yes | No |
IndexedDB | Yes | Yes | Yes |
Cache API | Yes | Yes | Yes |
WebSockets | Yes | Yes | Yes |
setTimeout/setInterval | Yes | Yes | No |
importScripts() | Yes (classic) | Yes (classic) | Yes (classic) |
ES import | Yes (module) | Yes (module) | No |
DOM | No | No | No |
window | No | No | No |
navigator | Partial | Partial | Partial |
location | Yes (WorkerLocation) | Yes | Yes |
self | Yes (WorkerGlobalScope) | Yes | Yes (ServiceWorkerGlobalScope) |
Module Workers and Imports
// type: 'module' — can use static and dynamic import const worker = new Worker('./worker.js', { type: 'module' }); // worker.js (module) import { heavyTask } from './utils.js'; onmessage = e => postMessage(heavyTask(e.data));
// Classic worker — use importScripts importScripts('https://cdn.example.com/lib.js', './utils.js');
Worklet (audio, paint, layout)
// CSS Paint Worklet (Houdini) await CSS.paintWorklet.addModule('paint-worklet.js'); // Audio Worklet await audioCtx.audioWorklet.addModule('processor.js'); const node = new AudioWorkletNode(audioCtx, 'my-processor'); // paint-worklet.js registerPaint('my-painter', class { paint(ctx, geom, props) { /* Canvas 2D API subset */ } });
Common Patterns
// Promisified worker request/response function callWorker(worker, message) { return new Promise((resolve, reject) => { const id = crypto.randomUUID(); const handler = e => { if (e.data.id !== id) return; worker.removeEventListener('message', handler); e.data.error ? reject(new Error(e.data.error)) : resolve(e.data.result); }; worker.addEventListener('message', handler); worker.postMessage({ ...message, id }); }); } // worker.js onmessage = async e => { try { const result = await doWork(e.data); postMessage({ id: e.data.id, result }); } catch (err) { postMessage({ id: e.data.id, error: err.message }); } };
// Inline worker from blob URL const code = `onmessage = e => postMessage(e.data * 2);`; const blob = new Blob([code], { type: 'application/javascript' }); const worker = new Worker(URL.createObjectURL(blob));