Modern JavaScript Reactive Systems, Async Patterns, and Browser APIs

Proxy and Reflect: Building Reactive Systems

Modern JavaScript frameworks like Vue 3 leverage Proxy and Reflect to implement reactivity more efficiently than the older Object.defineProperty approach. While defineProperty can only observe existing properties and requires manual intervention for additions or deletions, Proxy intercepts fundamental operations on objects, enabling comprehensive reactivity.

const targetMap = new WeakMap();

class Depend {
  constructor() {
    this.reactiveFns = new Set();
  }

  depend() {
    if (activeReactiveFn) {
      this.reactiveFns.add(activeReactiveFn);
    }
  }

  notify() {
    this.reactiveFns.forEach(fn => fn());
  }
}

function getDepend(target, key) {
  let map = targetMap.get(target);
  if (!map) {
    map = new Map();
    targetMap.set(target, map);
  }

  let depend = map.get(key);
  if (!depend) {
    depend = new Depend();
    map.set(key, depend);
  }
  return depend;
}

function reactive(obj) {
  return new Proxy(obj, {
    get(target, key, receiver) {
      const depend = getDepend(target, key);
      depend.depend();
      return Reflect.get(target, key, receiver);
    },
    set(target, key, value, receiver) {
      const oldValue = target[key];
      const result = Reflect.set(target, key, value, receiver);
      if (value !== oldValue) {
        const depend = getDepend(target, key);
        depend.notify();
      }
      return result;
    }
  });
}

let activeReactiveFn = null;

function watch(fn) {
  activeReactiveFn = fn;
  fn();
  activeReactiveFn = null;
}

const user = reactive({ name: "Liu Yifei", age: 25 });
watch(() => {
  console.log(`User: ${user.name}, Age: ${user.age}`);
});

user.name = "Zhang Ziyi"; // Triggers reactivity

The receiver parameter in get and set traps ensures that property accessors within the proxied object maintain correct this context, especially when accessed through inheritance or nested proxies.

Reflect.construct: Custom Constructor Behavior

Reflect.construct allows dynamic instantiation of a constructor with a different prototype chain, useful for advanced class extension patterns.

function Base() {}
function Derived() {}

const instance = Reflect.construct(Base, [], Derived);
console.log(instance.constructor.name); // "Derived"
console.log(instance instanceof Base);  // true
console.log(instance instanceof Derived); // true

Promise Fundamentals and Advanced Patterns

Promises provide a structured way to handle asynchronous operations, avoiding callback hell. The Promise API includes several static methods to manage multiple concurrent operations.

// Promise.allSettled: Collect results regardless of success/failure
const requests = [
  fetch('/api/user'),
  fetch('/api/posts'),
  fetch('/api/comments')
];

Promise.allSettled(requests).then(results => {
  results.forEach((result, index) => {
    if (result.status === 'fulfilled') {
      console.log(`Request ${index} succeeded:`, result.value);
    } else {
      console.log(`Request ${index} failed:`, result.reason);
    }
  });
});

// Promise.any: Resolve on first success, reject only if all fail
Promise.any([
  fetch('/fast-api'),
  fetch('/slow-api'),
  fetch('/broken-api')
]).then(response => {
  console.log('First successful response:', response);
}).catch(errors => {
  console.log('All requests failed:', errors.errors);
});

Iterator and Genreator Patterns

Itreators enable custom iteration logic, while generators simplify stateful iteration with yield.

class DataStream {
  constructor(data) {
    this.data = data;
  }

  *[Symbol.iterator]() {
    for (const item of this.data) {
      yield item;
    }
  }
}

const stream = new DataStream([10, 20, 30]);
for (const val of stream) {
  console.log(val); // 10, 20, 30
}

// Generator with control flow
function* sequence() {
  const first = yield 'start';
  const second = yield first + 100;
  return second + 200;
}

const gen = sequence();
console.log(gen.next());     // { value: 'start', done: false }
console.log(gen.next(50));   // { value: 150, done: false }
console.log(gen.next(300));  // { value: 500, done: true }

Async/Await: Simplifying Asynchronous Code

async/await syntactically transforms promise chains into synchronous-looking code.

async function loadUserData() {
  try {
    const user = await fetch('/api/user').then(r => r.json());
    const posts = await fetch(`/api/posts?userId=${user.id}`).then(r => r.json());
    return { user, posts };
  } catch (error) {
    console.error('Failed to load data:', error);
    throw error;
  }
}

loadUserData().then(result => console.log(result));

Event Loop: Microtasks vs. Macrotasks

JavaScript execution follows a single-threaded event loop with two task queues:

  • Microtasks: Promise.then, queueMicrotask, MutationObserver
  • Macrotasks: setTimeout, setInterval, I/O, UI rendering

Microtasks execute before the next macrotask, ensuring promise callbacks run immediately after the current synchronous code.

console.log('1');

setTimeout(() => console.log('2'), 0);

Promise.resolve().then(() => console.log('3'));

console.log('4');

// Output: 1, 4, 3, 2

Custom Error Handling and Type Validation

Robust applications validate inputs and throw meaningful errors.

class ValidationError extends Error {
  constructor(message, code) {
    super(message);
    this.code = code;
    this.name = 'ValidationError';
  }
}

function validateEmail(email) {
  if (!email || !email.includes('@')) {
    throw new ValidationError('Invalid email format', 'INVALID_EMAIL');
  }
  return email;
}

try {
  validateEmail('not-an-email');
} catch (err) {
  if (err instanceof ValidationError) {
    console.error(`Error ${err.code}: ${err.message}`);
  }
}

Module Systems: ESM vs. CommonJS

ES Modules (ESM) are statically analyzed, enabling tree-shaking and better optimization.

// math.js
export const add = (a, b) => a + b;
export const PI = 3.14159;

// main.js
import { add, PI } from './math.js';
import('./utils.js').then(utils => utils.format(PI));

Browser Storage Mechanisms

Modern browsers offer multiple storage options:

  • localStorage/sessionStorage: Key-value string storage
  • IndexedDB: Structured, indexed database for large datasets
// IndexedDB example
const dbRequest = indexedDB.open('AppDB', 1);

dbRequest.onupgradeneeded = event => {
  const db = event.target.result;
  const store = db.createObjectStore('users', { keyPath: 'id' });
  store.createIndex('email', 'email', { unique: true });
};

function addUser(user) {
  const transaction = db.transaction('users', 'readwrite');
  const store = transaction.objectStore('users');
  store.add(user);
}

Debouncing and Throttling

Optimize performance for high-frequency events like scroll or input.

function debounce(func, delay) {
  let timeoutId;
  return (...args) => {
    clearTimeout(timeoutId);
    timeoutId = setTimeout(() => func.apply(this, args), delay);
  };
}

function throttle(func, interval) {
  let lastExec = 0;
  return (...args) => {
    const now = Date.now();
    if (now - lastExec >= interval) {
      func.apply(this, args);
      lastExec = now;
    }
  };
}

window.addEventListener('scroll', throttle(updateUI, 16)); // ~60fps

Deep Cloning and Event Bus

Deep cloning handles nested structures, including Symbols and cyclic references.

function deepClone(obj, cache = new WeakMap()) {
  if (obj === null || typeof obj !== 'object') return obj;
  if (cache.has(obj)) return cache.get(obj);

  if (obj instanceof Date) return new Date(obj);
  if (obj instanceof Array) return obj.map(item => deepClone(item, cache));
  if (obj instanceof Map) return new Map([...obj].map(([k, v]) => [deepClone(k, cache), deepClone(v, cache)]));
  if (obj instanceof Set) return new Set([...obj].map(item => deepClone(item, cache)));

  const clone = Array.isArray(obj) ? [] : {};
  cache.set(obj, clone);

  for (const key in obj) {
    if (Object.prototype.hasOwnProperty.call(obj, key)) {
      clone[key] = deepClone(obj[key], cache);
    }
  }

  Object.getOwnPropertySymbols(obj).forEach(sym => {
    clone[sym] = deepClone(obj[sym], cache);
  });

  return clone;
}

// Event Bus
class EventBus {
  constructor() {
    this.events = {};
  }

  on(event, callback, context = this) {
    (this.events[event] = this.events[event] || []).push({ callback, context });
  }

  emit(event, ...args) {
    (this.events[event] || []).forEach(({ callback, context }) => callback.apply(context, args));
  }

  off(event, callback) {
    if (!this.events[event]) return;
    if (callback) {
      this.events[event] = this.events[event].filter(item => item.callback !== callback);
    } else {
      delete this.events[event];
    }
  }
}

DOM Events: Capture and Bubbling

Event propagation follows a three-phase model: capture, target, bubble.

const container = document.getElementById('container');
const button = document.getElementById('button');

container.addEventListener('click', () => console.log('Capture: container'), true);
button.addEventListener('click', () => console.log('Target: button'));
container.addEventListener('click', () => console.log('Bubble: container'), false);

// Click on button outputs:
// Capture: container
// Target: button
// Bubble: container

JSON Serialization and Deserialization

Use JSON.stringify with replacers and JSON.parse with revivers for custom serialization.

const data = {
  id: 1,
  name: 'Alice',
  createdAt: new Date(),
  toJSON() {
    return {
      id: this.id,
      name: this.name,
      created: this.createdAt.toISOString()
    };
  }
};

const json = JSON.stringify(data, null, 2);
const parsed = JSON.parse(json, (key, value) => {
  if (key === 'created') return new Date(value);
  return value;
});

Tags: Proxy Reflect Promise async-await generator

Posted on Tue, 25 Aug 2026 16:47:28 +0000 by alsal