A global variable in a language with parallel operation is often a terrible idea. The problem with globals and parallel operations is they are an inherent risk for race conditions that can have wild consequences.
In some languages, for example, a parallel write to a field is not guaranteed to be consistent. Let's assume in the above example `counter` was actually represented with 2 bytes. If two threads write an increment to it without a guard, there is no guarantee which thread will win the upper byte and which will win the lower byte. Most of the time it will be fine, but 1 in 10k there can be a bizarre counter leap (forwards or backwards) that'd be almost impossible to account for.
Now imagine this global is tucked away in a complex library somewhere and you've got an even bigger problem. Parallel calls to the library will just sometimes fail in ways that aren't easy to explain and, unfortunately, can only be fixed by the callee with a library wrapping synchronization construct. Nobody wants to do that.
All of these problems are masked by a language like Javascript. Javascript is aggressively single threaded (Everything is ran in a mutex!). Sure you can do concurrent things with callbacks/async blocks, but you can't mutate any application state from 2 threads. That makes a global variable work in most cases. It only gets tricky if you are dealing with a large amount of async while operating on the global variable.
Yes, mixing some concepts in programming is a terrible idea.
Perhaps this is also widely unpopular, but it's the parallelism that needs to be treated with care and the additional caution, as often the parallelism itself is the terrible idea.
Concurrent code often has unpredictable performance due to cache behavior and NUMA, unpredictable lock contention, and the fact that often there is no measure of whether the bottleneck is CPU or I/O.
What most people want from concurrency (like computing the response to independent HTTP requests) can be done by separate processes, and the OS can abstract the issues away. As another reference, the entire go language is designed around avoiding shared memory (and using message passing -- even though it doesn't use processes for separation it encourages coding like you did).
But also sharing memory between processes can be handled with care via mappings and using the OS.
A global variable in a language with parallel operation is often a terrible idea. The problem with globals and parallel operations is they are an inherent risk for race conditions that can have wild consequences.
In some languages, for example, a parallel write to a field is not guaranteed to be consistent. Let's assume in the above example `counter` was actually represented with 2 bytes. If two threads write an increment to it without a guard, there is no guarantee which thread will win the upper byte and which will win the lower byte. Most of the time it will be fine, but 1 in 10k there can be a bizarre counter leap (forwards or backwards) that'd be almost impossible to account for.
Now imagine this global is tucked away in a complex library somewhere and you've got an even bigger problem. Parallel calls to the library will just sometimes fail in ways that aren't easy to explain and, unfortunately, can only be fixed by the callee with a library wrapping synchronization construct. Nobody wants to do that.
All of these problems are masked by a language like Javascript. Javascript is aggressively single threaded (Everything is ran in a mutex!). Sure you can do concurrent things with callbacks/async blocks, but you can't mutate any application state from 2 threads. That makes a global variable work in most cases. It only gets tricky if you are dealing with a large amount of async while operating on the global variable.