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

Advanced State Synchronization

You will manage shared state safely across multiple concurrent promise resolution contexts.

Advanced State Synchronization

In this lesson, you will learn how to maintain a single source of truth when multiple asynchronous operations attempt to read and write shared data at the same time.

In Node.js, even though we use a single-threaded event loop, the asynchronous nature of promises means our code execution is constantly interrupted. When two different promises resolve at similar times and both try to update the same variable, you can end up with "race conditions" where the final state is unpredictable.

The Challenge of Concurrent Updates

Imagine you have a userCount variable. If two different asynchronous functions fetch data from a database and both try to increment this count, they might both read the same initial value (e.g., 5), perform their math, and save the result (e.g., 6) back to the variable. One update is effectively lost.

To synchronize this safely, we use a technique where we wrap our state updates in an execution queue or a "lock" mechanism. By ensuring that only one promise can enter the "critical section" of code at a time, we force concurrent operations to queue up.

let userCount = 0;
let queue = Promise.resolve(); // Our synchronization lock

function updateCount(increment) {
  // Chain the operation to the end of the current queue
  queue = queue.then(async () => {
    const current = userCount;
    // Simulate async work
    await new Promise(resolve => setTimeout(resolve, 10));
    userCount = current + increment;
    console.log(`New count: ${userCount}`);
  });
  
  return queue;
}

// Even if called simultaneously, they execute in sequence
updateCount(1);
updateCount(5);

Why This Matters

By re-assigning our queue variable to the result of .then(), we create a chain where every new request waits for the previous one to settle. This ensures that the state read by the second operation is always the updated state left by the first. Even though the processes happen asynchronously, the mutation of your shared state becomes deterministic. This pattern effectively turns your concurrent promise flow into a serialized pipeline for sensitive data.

Key Takeaway: To prevent state corruption in async code, serialize your state updates by chaining them to a persistent promise chain that acts as a sequential lock.