Course contentsShow
← Node.js
Lesson 179 of 1,9803. Asynchronous Patterns and Flow ControlFree lesson

Optimizing Concurrency with Promise.race

You will manage timeout scenarios and competitive execution patterns to prevent resource saturation in server-side tasks.

In this lesson, you will learn how to prevent your Node.js applications from hanging indefinitely by using Promise.race to enforce strict time limits on asynchronous operations.

Why Concurrency Needs Boundaries

In a server-side environment, tasks like database queries or external API calls can sometimes hang due to network congestion or server failures. If your code waits forever for a single operation, it can block your event loop or consume precious memory, eventually leading to resource saturation.

Promise.race is the perfect tool to handle this. It accepts an array of promises and "settles" as soon as the first one completes—whether that completion is a success or a timeout.

Implementing a Timeout Pattern

To prevent resource saturation, we can "race" our actual task against a timer. If the timer wins, we know our task took too long and can safely abandon the effort.

const fetchUserData = (userId) => {
  return new Promise((resolve) => setTimeout(() => resolve("User Data"), 5000));
};

const timeout = (ms) => new Promise((_, reject) => 
  setTimeout(() => reject(new Error("Request timed out")), ms)
);

async function safeExecution() {
  try {
    // Race the task against a 2-second timeout
    const result = await Promise.race([fetchUserData(), timeout(2000)]);
    console.log(result);
  } catch (error) {
    console.error("Task failed:", error.message);
  }
}

safeExecution();

Preventing Resource Saturation

When you use this pattern, you are effectively setting a "fail-fast" boundary. Instead of keeping a connection open for 5 seconds and potentially causing a backup of incoming requests, your server rejects the stalled process after 2 seconds. This frees up the internal resources—like file descriptors or socket connections—that would otherwise be tied up waiting for a response that may never arrive. By managing these competitive execution patterns, your server remains responsive even when individual dependencies underperform.

Key Takeaway: Use Promise.race to pair long-running tasks with a timeout promise, ensuring your server remains performant and protected from hanging dependencies.