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
clockbefore and after theawaitline might not be the same. Hence, if the value ofclockis currently11and you quickly call theincrementSlowlyfunction multiple times then here's what'll happen.clockjust beforeawaitclockjust afterawaitNotice that only the first call has the same
clockvalue before and after theawaitline. The second call has differentclockvalues because the first call incrementsclockafter it is resumed.One way to solve this problem would be to move the
clock++line before theawaitline as follows.Now, if the value of
clockis currently11and you quickly call theincrementSlowlyfunction multiple times then here's what'll happen.clockjust beforeawaitclockjust afterawaitSince the value of
clockis not updated afterincrementSlowlyis resumed, it will always remain consistent. This is what your example does too. It increments the value ofclockbefore callingdelay. You also introduce an intermediate variablexbut 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 theawaitwith asetTimeoutin 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.