{"slug": "part-1-taming-asynchronous-javascript-how-to-build-a-mailbox-queue", "title": "Part 1: Taming Asynchronous JavaScript: How to Build a \"Mailbox\" Queue", "summary": "The article explains how to build a JavaScript \"Mailbox\" queue using Promises to manage asynchronous data flow when a producer sends data faster than a consumer can process it. The implementation stores incoming messages in an array and uses a waiter array to pause the consumer via unresolved Promises until new data arrives. The author notes that while this approach works for typical use cases, it can freeze a server when handling very large volumes of data, which will be addressed in a follow-up article.", "body_md": "Have you ever tried to catch water from a fire hydrant with a paper cup?\n\nThat is exactly what it feels like when you are building a JavaScript app and data starts coming in way faster than your code can process it. Maybe you are handling a flood of incoming webhooks, reading a massive file, or listening to a busy WebSocket.\n\nIf your data sender is faster than your data receiver, things break.\n\nWhat you need is a waiting room. A place where incoming data can chill out until your app is ready to handle it. In computer science, this is called a \"Queue\" or a \"Channel.\" Today, we are going to build one from scratch. Let's call it our Mailbox.\n\nThe Idea Behind the Mailbox\n\nThink of a literal physical mailbox.\n\nThe Mail Carrier (The Producer): Drops letters into the box. They don't care if you are home; they just drop the mail and leave.\n\nYou (The Consumer): You check the mailbox. If there is mail, you take it. If the box is empty, you just wait until the mail carrier shows up.\n\nWe can recreate this exact relationship in JavaScript using Promises. Here is the code. It might look a little magical at first, but we will break it down right after!\n\n```\nexport class Mailbox {\n  constructor() {\n    this.messages = []\n    this.waiters = []\n    this.closed = false\n  }\n\n  push(message) {\n    if (this.closed) {\n      throw new Error(\"Mailbox is closed\")\n    }\n\n    // Deliver directly to waiting consumer\n    if (this.waiters.length > 0) {\n      const resolve = this.waiters.shift()\n      resolve(message)\n      return\n    }\n\n    this.messages.push(message)\n  }\n\n  async pop() {\n    // Message already available\n    if (this.messages.length > 0) {\n      return this.messages.shift()\n    }\n\n    // Closed mailbox\n    if (this.closed) {\n      return null\n    }\n\n    // Wait for future message\n    return new Promise((resolve) => {\n      this.waiters.push(resolve)\n    })\n  }\n\n  close() {\n    this.closed = true\n\n    // Wake all waiting consumers\n    while (this.waiters.length > 0) {\n      const resolve = this.waiters.shift()\n      resolve(null)\n    }\n  }\n\n  get size() {\n    return this.messages.length\n  }\n\n  async *[Symbol.asyncIterator]() {\n    while (true) {\n      const msg = await this.pop()\n\n      if (msg === null) {\n        break\n      }\n\n      yield msg\n    }\n  }\n}\n```\n\n## The \"Aha!\" Moment: Pausing JavaScript\n\nThe coolest part of this code is inside the ** pop()** method.\n\nUsually, when we use a Promise in JavaScript, it's for something like **fetch()**\n\nYou make a request, and eventually, it resolves.\n\nBut here, we are doing something sneaky. If the mailbox is empty, we create a new Promise, but we take its resolve function and shove it into our **this.waiters** array. We are essentially bottling up the ability to finish the Promise for later.\n\nYour code effectively pauses. It just sits there, waiting.\n\nThen, when the ** push()** method gets called, it looks inside this.waiters, pulls out that bottled-up resolve function, and triggers it with the new message. Boom! Your paused code instantly wakes up with the data.\n\n## How to Use It\n\nBecause we added that weird looking **[Symbol.asyncIterator]** at the bottom of the class, using our Mailbox is beautifully simple:\n\n``` js\nconst mailbox = new Mailbox();\n\n// 1. You: waiting for mail\nasync function readMail() {\n  // This loop will naturally pause and wait for new messages!\n  for await (const msg of mailbox) {\n    console.log(\"Just got:\", msg);\n  }\n  console.log(\"No more mail coming!\");\n}\nreadMail();\n\n// 2. The Mail Carrier: dropping off mail at random times\nmailbox.push(\"Letter 1\");\nsetTimeout(() => mailbox.push(\"Letter 2\"), 1000);\nsetTimeout(() => mailbox.close(), 2000);\n```\n\nThis setup works flawlessly for everyday tasks. But there is a hidden monster in this code.\n\nIf you get too popular—say, someone drops 1,000,000 letters into your mailbox at once—this exact code will completely freeze your server for minutes. In Part 2, we are going to find out exactly why JavaScript hates huge arrays, and how to fix our Mailbox to handle millions of messages in a fraction of a second.\n\nThe link to the repo of full source code of this: [https://github.com/pckrishnadas88/mailbox](https://github.com/pckrishnadas88/mailbox)", "url": "https://wpnews.pro/news/part-1-taming-asynchronous-javascript-how-to-build-a-mailbox-queue", "canonical_source": "https://dev.to/pckrishnadas88/part-1-taming-asynchronous-javascript-how-to-build-a-mailbox-queue-2jok", "published_at": "2026-05-23 10:13:18+00:00", "updated_at": "2026-05-23 10:32:38.489252+00:00", "lang": "en", "topics": ["developer-tools"], "entities": [], "alternates": {"html": "https://wpnews.pro/news/part-1-taming-asynchronous-javascript-how-to-build-a-mailbox-queue", "markdown": "https://wpnews.pro/news/part-1-taming-asynchronous-javascript-how-to-build-a-mailbox-queue.md", "text": "https://wpnews.pro/news/part-1-taming-asynchronous-javascript-how-to-build-a-mailbox-queue.txt", "jsonld": "https://wpnews.pro/news/part-1-taming-asynchronous-javascript-how-to-build-a-mailbox-queue.jsonld"}}