Chapter 1 of ?
js 11 min read

JavaScript Mastery — Chapter 26: Advanced Async Patterns & Web Workers

Module 6: Asynchronous JavaScript & Workers Chapter 26 30 min read

Advanced Async Patterns & Web Workers

Scale your asynchronous architecture to production. Master the 4 Promise combinators (all, allSettled, race, any), implement clean cancellation with AbortController, and escape single-threaded UI freezes with true browser multi-threading via Web Workers.

Multi-Threaded Web Worker & Message Passing Architecture

Web Workers run on dedicated operating-system background threads completely isolated from the browser's Main UI thread. They communicate exclusively via serialized message passing (or zero-copy Transferable Objects).

MAIN BROWSER UI THREAD • DOM Rendering & CSS Layout • 60fps Scroll & User Touch Events worker.postMessage({ data: rawImg }); worker.onmessage = e => updateUI(); Stays 100% fluid & unblocked! postMessage postMessage BACKGROUND WEB WORKER • Heavy CPU Math / Crypto Hashing • 4K Video / Image Filter Processing self.onmessage = e => compute(); ⛔ NO DOM ACCESS (No document/window) True OS multi-core parallel computing

1. The 4 Promise Combinators

When orchestrating multiple concurrent asynchronous operations, choose the exact combinator matching your business failure tolerance:

Combinator Settles When... Fails When... Ideal Production Use Case
Promise.all() ALL promises fulfill. Returns array of all values. ANY single promise rejects (Fail-Fast). Transactional batches (e.g. user details + cart + shipping must all succeed).
Promise.allSettled() ALL promises settle (fulfilled OR rejected). Never fails! Returns array of { status, value/reason }. Bulk reporting, sending bulk notifications, dashboard widget aggregations.
Promise.race() Whichever promise settles FIRST (fulfilled OR rejected). If the first promise rejects. Request timeouts (race fetch against a sleep rejection timer).
Promise.any() Whichever promise FULFILLS first (ignores rejections). Only if ALL promises reject (throws AggregateError). Fastest mirror lookup (querying 3 CDN endpoints for the quickest response).

Practical Combinator Examples

const fetchUser = fetch('/api/user').then(r => r.json());
const fetchPrefs = fetch('/api/prefs').then(r => r.json());

// 1. Promise.allSettled: Never crashes your entire dashboard!
const results = await Promise.allSettled([fetchUser, fetchPrefs]);
results.forEach(res => {
  if (res.status === 'fulfilled') {
    console.log('Success payload:', res.value);
  } else {
    console.warn('Failed gracefully with reason:', res.reason.message);
  }
});

// 2. Promise.any: Query fastest CDN mirror
const mirrorA = fetch('https://cdn-us.aicodelab.tech/asset.png');
const mirrorB = fetch('https://cdn-eu.aicodelab.tech/asset.png');
const fastestResponse = await Promise.any([mirrorA, mirrorB]);
Try it in Playground

2. Request Cancellation with AbortController

In modern web development, users frequently type in search boxes or navigate away before a request finishes. Without cancellation, outdated network responses overwrite newer state (race condition). The AbortController API solves this cleanly:

Canceling In-Flight Fetch Requests

const controller = new AbortController();

async function performSearch(query) {
  try {
    const res = await fetch(`/api/search?q=${query}`, {
      // Connect fetch to abort signal:
      signal: controller.signal
    });
    return await res.json();
  } catch (err) {
    if (err.name === 'AbortError') {
      console.log('Search aborted by user or superseded by new query.');
    } else {
      throw err;
    }
  }
}

// User types a new letter: abort the previous search instantly!
controller.abort();
Try it in Playground

3. Web Workers: True Multi-Threading in JavaScript

If your web application performs intensive mathematical computing (image manipulation, cryptographic proof generation, physics simulation), running it on the main thread drops frames to 0fps. Web Workers execute code on an independent OS thread:

Instantiating and Communicating with a Worker

// In main.js (UI Thread):
const worker = new Worker('worker.js');

// Send work payload to background thread:
worker.postMessage({ numbers: [999999, 1000000] });

// Listen for results from worker thread:
worker.onmessage = (event) => {
  console.log('Prime count computed by background thread:', event.data.result);
};

// ----------------------------------------------------
// In worker.js (Background Thread - Zero DOM access):
self.onmessage = (event) => {
  const [min, max] = event.data.numbers;
  // Heavy computation runs on OS core without freezing UI:
  let count = 0;
  for (let i = min; i <= max; i++) {
    if (isPrime(i)) count++;
  }
  // Post message back to UI thread:
  self.postMessage({ result: count });
};
Try it in Playground

Interactive Mini-Lab: Promise Combinator Battle Arena

Three concurrent tasks run: Task A (Fast, 200ms), Task B (Slow, 600ms), and Task C (Fails at 100ms). Compare how each combinator resolves:

// Click a combinator button above to evaluate concurrent behavior...

Hands-on Challenge: Concurrency Pool Limiter

Coding Challenge

Firing 100 HTTP requests simultaneously via Promise.all() can crash backend servers or trigger 429 Too Many Requests rate-limiting. Build a concurrency pool function asyncPool(limit, items, asyncFn) that executes tasks in parallel with a strict cap of limit concurrent jobs:

Concurrency Pool Requirements
  1. Process an array of items using asyncFn(item).
  2. At any given moment, no more than limit promises should be pending.
  3. When one job finishes, the next pending item immediately begins.
  4. Return an array of all results in the same index order as the input items!
Production Concurrency Pool Implementation
/**
 * Runs tasks with a strict concurrency limit using modern Promise pooling.
 */
async function asyncPool(limit, items, iteratorFn) {
  const results = [];
  const executing = new Set();

  for (const item of items) {
    // 1. Kick off the task and store its promise:
    const p = Promise.resolve().then(() => iteratorFn(item));
    results.push(p);
    executing.add(p);

    // Clean up when settled:
    const clean = () => executing.delete(p);
    p.then(clean, clean);

    // 2. If pool limit reached, wait for at least one executing task to finish:
    if (executing.size >= limit) {
      await Promise.race(executing);
    }
  }

  // 3. Wait for all queued results to settle:
  return Promise.all(results);
}

// Verification:
const urls = [1, 2, 3, 4, 5, 6, 7, 8];
const downloadFile = id => new Promise(res => {
  console.log(`Starting file #${id}`);
  setTimeout(() => {
    console.log(`Finished file #${id}`);
    res(`Data #${id}`);
  }, 200);
});

// Run with max 2 concurrent downloads at a time:
asyncPool(2, urls, downloadFile).then(allResults => {
  console.log('All files downloaded:', allResults);
});

Chapter 26 Knowledge Check

Validate your mastery of Promise combinators, cancellation, and Web Worker threads.

1. Which Promise combinator will NEVER reject, and always returns an array of objects detailing the outcome of every input promise?
Promise.all()
Promise.allSettled()
Promise.race()
Promise.any()
2. What is the fundamental difference between Promise.race() and Promise.any()?
Promise.race() only works on network requests; Promise.any() works on timers.
Promise.race() settles on the first promise to settle (even if it rejects); Promise.any() ignores rejections and resolves on the first promise to FULFILL.
Promise.any() is deprecated in favor of Promise.all().
There is no difference.
3. Can code running inside a Web Worker thread directly manipulate the HTML DOM (e.g. document.getElementById())?
Yes, Web Workers have full read and write access to the DOM.
No, Web Workers have no access to window or document; they must communicate results to the main thread via postMessage().
Yes, but only in Google Chrome.
Yes, if passed with transferable: true.
4. What browser API is used to cancel in-flight fetch() HTTP requests?
fetch.cancel()
AbortController and its signal property.
window.stopNetwork()
clearTimeout()
5. If all input promises reject when calling Promise.any(), what error is thrown?
TypeError
SyntaxError
AggregateError (containing an errors array of all rejection reasons).
RangeError
Done with this chapter?
Mark it complete to track your progress and unlock your certificate.
Next Up
—

Learner Reviews

Write a Review
Share your experience to help other learners.
Your Rating *
★ ★ ★ ★ ★