Search code examples
javascriptfunctional-programmingimmutabilitymutable

What is a typical class of issues that is caused by mutable data types in a single-threaded environment?


JS models concurrency by an event loop. As a result there are no race conditions. So what are the drawbacks of the following type safe operation in the main scope of a program that would justify any caveat:

const m = new Map([["foo", true]]);

//...

m.set("bar", false);

Even if I'd empty m this shouldn't raise any issues, since every operation that depends on m should consider the empty case anyway.

Maybe someone can illustrate a typical class of issues that comes along with mutable data types.

I know that this question might be too opinion based so please feel free to close it if you think it doesn't fit SO.

Thanks in advance!


Solution

  • JS models concurrency by an event loop. As a result there are no race conditions.

    Let's stop right there. You might not get two different threads that try to access the same memory location at the same time, but you can still have concurrent parts of your program access mutable state asynchronously and ignore the fact that they are not alone. This is still a race condition.

    A simple example:

    var clock = out.value = 0;
    
    async function incrementSlowly() {
      if (clock == 12)
        clock = 0; // reset
      await delay(1000);
      clock++;
      out.value = clock;
    }
    function delay(t) { return new Promise(resolve => setTimeout(resolve, t)); }
    <output id="out"></output>
    <button onclick="incrementSlowly()">Tick!</button>

    The clock value will never get greater than 12? Try yourself what happens when you hit the button quickly.

    The multiple invocations of the incrementSlowly function are running independently, and are doing the check at the wrong time - during their delay, another instance might already have incremented the clock again.

    In this example, I've used a mutable variable, but it's the same for using mutable data structures. And it's not always as obvious when there are multiple agents accessing the structure through different methods.

    Using immutable data structures forces you to make stateful operations explicit, and it would have become clear that incrementSlowly actually accesses the state twice.