Asynchronous operations entail race conditions provided they are evaluated in parellel. However, a counter should always be
evaluated in sequence as per logic. I'd claim this reveals another flaw of Javascript's Promise
type: It is actually a blend of
the two types (sequential) Task
and Parallel
. While the former is a monad the latter is only an applicative.
Bergi has a point, of course. Asynchronous operations in parallel should solely rely on immutable data. However, operations that are inherently sequential should be evaluated as such and thus it doesn't matter if the underlying data is immutable or not.
I am not that versed in dealing with promises, so I probably doesn't use the type correctly. However, it should be enough to illustrate my point.
let clock = 0;
const incSlowly = o => new Promise(res => {
if (clock == 12)
clock = 0; // reset
delay(1000, x => (o.value = x, res(x)), clock++);
});
const delay = (t, f, x) => {
return new Promise(res => setTimeout(y => res(f(y)), t, x));
};
<output id="out">0</output>
<button onclick="incSlowly(document.getElementById('out'))">Tick!</button>
Consider Bergi's original example.
The problem with this example is that the value of
clock
before and after theawait
line might not be the same. Hence, if the value ofclock
is currently11
and you quickly call theincrementSlowly
function multiple times then here's what'll happen.clock
just beforeawait
clock
just afterawait
Notice that only the first call has the same
clock
value before and after theawait
line. The second call has differentclock
values because the first call incrementsclock
after it is resumed.One way to solve this problem would be to move the
clock++
line before theawait
line as follows.Now, if the value of
clock
is currently11
and you quickly call theincrementSlowly
function multiple times then here's what'll happen.clock
just beforeawait
clock
just afterawait
Since the value of
clock
is not updated afterincrementSlowly
is resumed, it will always remain consistent. This is what your example does too. It increments the value ofclock
before callingdelay
. You also introduce an intermediate variablex
but that's not necessary.Notice that this has nothing to do with
Promise
,Task
,Parallel
,Applicative
, orMonad
, and it has everything to do with concurrency. As Bergi mentions, you can replace theawait
with asetTimeout
in the above examples and it would still work the same.In your original question, you wrote the following.
This is a false statement. If you have concurrency and mutability, you can have race conditions. An event loop doesn't magically prevent race conditions. Bergi's answer shows an example of a race condition in JavaScript, thereby disproving your assumption that, “there are no race conditions.”
Hence, to answer your question.
Race conditions.