Engrain by ByteRush

WEB.06.01 Mid-level· 13 minCore — asked often

Web Workers, SharedArrayBuffer and cross-origin isolation

Move heavy work off the main thread with Web Workers: postMessage and structured cloning, transferable objects, module workers, what workers cannot touch, RPC wrappers, and why SharedArrayBuffer needs COOP and COEP.

Comes up in: Web fundamentals, Frontend system design

Learn#

Fatima’s expense app freezes for 3 seconds when users upload a 40 MB bank statement. The CSV parser runs on the main thread, so clicks, typing and even the spinner stop (JS.01.07).

A web worker runs JavaScript on a separate thread. The page and the worker share no variables; they talk by sending messages with postMessage.

Messages are copied with the structured clone algorithm, the same one structuredClone() uses:

const order = { placed: new Date(0), tags: new Set(["gift"]), pixels: new ArrayBuffer(1024) };
const copy = structuredClone(order, { transfer: [order.pixels] });
console.log(copy.placed instanceof Date, copy.tags.has("gift"));
console.log("after transfer:", order.pixels.byteLength, "→", copy.pixels.byteLength);
try { structuredClone({ onSave() {} }); } catch (e) { console.log(e.name); }

Output (verified)

true true
after transfer: 0 → 1024
DataCloneError

Dates and Sets survive; functions cannot be sent.

A transferable, such as an ArrayBuffer, moves instead of copying: the sender’s copy becomes empty, so big data costs nothing to send.

Parsing a big file in a workerStep 1 of 4

State

  • main thread: UI
  • worker thread: idle

Console

    empty

    The page creates a worker from parse-worker.js. It runs on its own thread, with its own global scope.

    Read the steps as text
    1. The page creates a worker from parse-worker.js. It runs on its own thread, with its own global scope.
    2. The page sends the uploaded file's ArrayBuffer and lists it as transferable. No copy is made.
    3. The worker parses 400,000 CSV rows for 3 seconds. The main thread stays free: scrolling and typing still work.
    4. The worker posts back a small summary. The main thread receives it in a message event and renders it.

    Predict#

    Predict first

    Inside a dedicated worker, you call document.querySelector('#total'). What happens?

    Interview angle#

    Say it like this — about 30 seconds

    “A Web Worker runs JavaScript on its own thread, so heavy work such as parsing or image processing does not block input. It cannot touch the DOM. The page and the worker talk with postMessage, which copies data using structured clone, so big objects cost time; transferable objects such as ArrayBuffer move instead, leaving the sender’s copy empty. SharedArrayBuffer shares memory between threads, but since Spectre it works only on cross-origin isolated pages, which need COOP same-origin and COEP headers.”

    Stays on your device. Nothing is uploaded.
    Follow-up What is a Web Worker, and what can it not do?
    A background thread for JavaScript. It cannot access the DOM, window or document; it talks to the page by messages.
    Follow-up What are transferable objects?
    Objects such as ArrayBuffer, MessagePort and OffscreenCanvas that move ownership to the receiver instead of being copied.
    Follow-up Why does SharedArrayBuffer need cross-origin isolation?
    Shared memory gives precise timers usable for Spectre attacks; isolation guarantees no other site’s data shares your process.

    Practice#

    On your own

    Write a small RPC layer over a MessagePort, the idea behind Comlink, so calling a worker feels like calling an async function. The tests use a real MessageChannel.

    Done when

    • wrap(port).sum(2, 3) resolves to what the exposed sum returns, also for async methods.
    • Errors thrown in the exposed method reject the caller’s promise with the same message.
    • Several calls in flight at once each get their own answer.
    Your solution
    Ctrl/⌘ + Enter

    Remember#

    Try to answer each card before you open it. Then add them to your reviews.

    What can a web worker not access?

    The DOM: no document or window.

    How does postMessage copy data?

    With the structured clone algorithm.

    What happens to an ArrayBuffer after you transfer it?

    It moves; the sender's copy becomes empty (byteLength 0).

    Which two headers make a page cross-origin isolated?

    COOP: same-origin and COEP: require-corp (or credentialless).

    Go deeper

    Four layers. Stop at the one your interview needs; each ends with a quick check.

    The core ideaEvery round
    new Worker(new URL("./parse.js", import.meta.url), { type: "module" }) starts a worker that can use import: Chrome 80, Firefox 114, Safari 15.
    Workers get fetch, timers, IndexedDB, WebSockets and OffscreenCanvas, but no DOM.
    A SharedWorker is one worker shared by several tabs of the same origin.
    Check yourself: How do you start a module worker?

    new Worker(url, { type: 'module' })

    How it works underneathSenior rounds

    Raw messages get messy, so libraries such as Comlink wrap them as RPC: each call gets an id, the worker replies with that id, and the caller’s promise settles Source: MDN: Using Web Workers.
    Cloning a large object can take longer than the work saved, so measure.

    Check yourself: Why match replies by id in a worker RPC?

    Several calls can be in flight, and replies may come back in any order.

    Edge cases and trapsWhere people slip
    SharedArrayBuffer was disabled after Spectre in 2018 and came back only for isolated pages. COOP same-origin separates your window from cross-origin popups. COEP require-corp loads only cross-origin resources that opt in.
    Together they make cross-origin isolation; check self.crossOriginIsolated Source: web.dev: Making your website cross-origin isolated using COOP and COEP.
    Check yourself: What does COEP: require-corp make the page do?

    Load cross-origin resources only if they opt in with CORS or a CORP header.

    In productionStaff-level answers
    With shared memory, Atomics gives safe reads and writes, and Atomics.wait and notify let threads sleep and wake; the main thread may not block, so it uses Atomics.waitAsync.
    You need this for WebAssembly threads (video, image and ML libraries), rarely for app code.
    Check yourself: What does Atomics.wait do, and where is it not allowed?

    Blocks a thread until notified; not allowed on the main thread.

    Finished the five steps? Mark it, and its cards join your reviews.

    Last checked 2026-11-01. Spot a mistake? Use “Report an error” below — fixes are listed on the corrections page.

    Interview questions this answers

    • What is a Web Worker and what can it not do?
    • What are transferable objects?
    • Why does SharedArrayBuffer need cross-origin isolation?