System Design And Data Structures

async-queue

An AsyncQueue class that processes asynchronous tasks in sequence. Each task is a function returning a Promise, and tasks are executed one after another in the order they were added.

Useful for controlling async flows like background jobs, rate-limited API calls, or sequential workflows.

class AsyncQueue {
  constructor() {
    this.queue = [];
  }
 
  enqueue(task) {
    if (typeof task !== "function") {
      throw new Error("Task must be a function returning a promise");
    }
    this.queue.push(task);
  }
 
  async process() {
    const results = [];
 
    for (const task of this.queue) {
      const result = await task();
      results.push(result);
    }
 
    return results;
  }
}
 
const queue = new AsyncQueue();
queue.enqueue(
  () =>
    new Promise((resolve) => {
      setTimeout(() => resolve("Task 1"), 1000);
    }),
);
queue.enqueue(
  () =>
    new Promise((resolve) => {
      setTimeout(() => resolve("Task 2"), 500);
    }),
);
 
queue.process().then((results) => console.log(results));
// ['Task 1', 'Task 2']

Queue

A simple Queue class that follows the FIFO (First In, First Out) principle. Elements are added to the end and removed from the front.

Commonly used in scheduling, task queues, and breadth-first search algorithms.

class Queue {
  constructor() {
    this.collection = [];
  }
 
  enqueue(element) {
    this.collection.push(element);
    return this;
  }
  dequeue() {
    this.collection.shift();
    return this;
  }
  front() {
    return this.collection[0];
  }
  get size() {
    return this.collection.length;
  }
  isEmpty() {
    return this.collection.length === 0;
  }
}
 
const queue = new Queue();
queue.enqueue("one").enqueue("two");
// [ 'one', 'two' ]
queue.dequeue(); // [ 'two' ]
queue.enqueue("three").enqueue("four");
// [ 'two', 'three', 'four' ]
queue.front(); // two
queue.size; // 3
queue.isEmpty(); // false

Redux

An implementation of the Redux pattern using vanilla JavaScript. This custom Store class manages state using a reducer and supports dispatching actions and subscribing to state changes.

Redux is commonly used for predictable state management in frontend applications like React.

class Store {
  constructor(reducer, initialState) {
    this.reducer = reducer;
    this.state = initialState;
    this.listeners = [];
  }
 
  getState() {
    return this.state;
  }
 
  dispatch(action) {
    this.state = this.reducer(this.state, action);
    this.listeners.forEach((listener) => listener());
  }
 
  subscribe(listener) {
    this.listeners.push(listener);
    return () => {
      console.log("unsubscribed");
      this.listeners = this.listeners.filter((l) => l !== listener);
    };
  }
}
 
// Example reducer function
const reducer = (state, action) => {
  switch (action.type) {
    case "INCREMENT":
      return { ...state, count: state.count + 1 };
    case "DECREMENT":
      return { ...state, count: state.count - 1 };
    default:
      return state;
  }
};
 
const initialState = { count: 0 };
 
const store = new Store(reducer, initialState);
 
// Subscribe to state changes
const unsubscribe = store.subscribe(() => {
  console.log(store.getState());
});
 
// Dispatch actions
console.log(store.getState());
store.dispatch({ type: "INCREMENT" });
store.dispatch({ type: "INCREMENT" });
store.dispatch({ type: "DECREMENT" });
 
// Unsubscribe from state changes
unsubscribe();
 
// { count: 0 }
// { count: 1 }
// { count: 2 }
// { count: 1 }
// unsubscribed

Stack Palindrome

This function uses a stack to check if a given word is a palindrome. It pushes each character onto the stack, then pops them in reverse order to compare with the original string.

function isPalindrome(word) {
  const letters = []; // this is our stack
  let reversedWord = "";
 
  // put letters of word into stack
  for (let i = 0; i < word.length; i++) {
    letters.push(word[i]);
  }
 
  // pop off the stack in reverse order
  for (let i = 0; i < word.length; i++) {
    reversedWord += letters.pop();
  }
 
  return reversedWord === word;
}
 
console.log(isPalindrome("racecar")); // true
console.log(isPalindrome("cheatsheet")); // false

Stack

A basic Stack class implementation that follows LIFO (Last In, First Out) behavior. Includes common methods such as push, pop, peek, and size.

Useful for:

class Stack {
  constructor() {
    this.storage = [];
  }
 
  push(value) {
    this.storage.push(value);
    return this;
  }
 
  pop() {
    this.storage.pop();
    return this;
  }
 
  size() {
    return this.storage.length;
  }
 
  peek() {
    return this.storage[this.storage.length - 1];
  }
}
 
const myStack = new Stack();
 
myStack.push(1).push(2).push(3);
console.log(myStack.peek()); // 3
myStack.pop();
console.log(myStack.peek()); // 2
myStack.push("js-cheatsheet");
console.log(myStack.size()); // 3
console.log(myStack.peek()); // js-cheatsheet

Twitter

A simplified simulation of Twitter's core features using JavaScript classes. Implements functionality for posting tweets, following/unfollowing users, and generating a personalized news feed.

Key features:

Demonstrates how to work with Map, Set, arrays, and sorting for feed generation.

class Twitter {
  constructor() {
    this.follows = new Map();
    this.tweets = new Map();
    this.timestamp = 0;
  }
 
  postTweet(userId, tweetId) {
    if (!this.tweets.has(userId)) {
      this.tweets.set(userId, []);
    }
    this.tweets.get(userId).unshift({ tweetId, timestamp: this.timestamp++ });
 
    if (!this.follows.has(userId)) {
      this.follows.set(userId, new Set());
      // User follows themselves
      this.follows.get(userId).add(userId);
    }
  }
 
  getNewsFeed(userId) {
    if (!this.follows.has(userId)) return [];
 
    const followed = this.follows.get(userId);
    const feed = [];
 
    for (const followeeId of followed) {
      if (this.tweets.has(followeeId)) {
        feed.push(...this.tweets.get(followeeId));
      }
    }
 
    feed.sort((a, b) => b.timestamp - a.timestamp);
    return feed.map((tweet) => tweet.tweetId);
  }
 
  follow(followerId, followeeId) {
    if (!this.follows.has(followerId)) {
      this.follows.set(followerId, new Set());
      // User follows themselves
      this.follows.get(followerId).add(followerId);
    }
    this.follows.get(followerId).add(followeeId);
  }
 
  unfollow(followerId, followeeId) {
      // Cannot unfollow oneself
    if (followerId === followeeId) return;
    if (this.follows.has(followerId)) {
      this.follows.get(followerId).delete(followeeId);
    }
  }
}
 
const twitter = new Twitter();
 
// User 1 posts a tweet with ID 5
twitter.postTweet(1, 5);
 
// User 1 retrieves their news feed (should contain only tweet 5)
console.log(twitter.getNewsFeed(1)); // [5]
 
// User 1 follows User 2
twitter.follow(1, 2);
 
// User 2 posts a tweet with ID 6
twitter.postTweet(2, 6);
 
// User 1 retrieves their news feed (should contain tweets 6 and 5, most recent first)
console.log(twitter.getNewsFeed(1)); // [6, 5]
 
// User 1 unfollows User 2
twitter.unfollow(1, 2);
 
// User 1 retrieves their news feed (should only contain their own tweet, 5)
console.log(twitter.getNewsFeed(1)); // [5]