26

Suppose I load some Flash movie that I know at some point in the future will call window.flashReady and will set window.flashReadyTriggered = true.

Now I have a block of code that I want to have executed when the Flash is ready. I want it to execute it immediately if window.flashReady has already been called and I want to put it as the callback in window.flashReady if it has not yet been called. The naive approach is this:

if(window.flashReadyTriggered) {
  block();
} else {
  window.flashReady = block;
}

So the concern I have based on this is that the expression in the if condition is evaluated to false, but then before block() can be executed, window.flashReady is triggered by the external Flash. Consequently, block is never called.

Is there a better design pattern to accomplish the higher level goal I'm going for (e.g., manually calling the flashReady callback)? If not, am I safe, or are there other things I should do?

Elias Zamaria
  • 96,623
  • 33
  • 114
  • 148
Steven
  • 17,796
  • 13
  • 66
  • 118

5 Answers5

34

All Javascript event handler scripts are handled from one master event queue system. This means that event handlers run one at a time and one runs until completion before the next one that's ready to go starts running. As such, there are none of the typical race conditions in Javascript that one would see in a multithreaded language where multiple threads of the language can be running at once (or time sliced) and create real-time conflict for access to variables.

Any individual thread of execution in javascript will run to completion before the next one starts. That's how Javascript works. An event is pulled from the event queue and then code starts running to handle that event. That code runs by itself until it returns control to the system where the system will then pull the next event from the event queue and run that code until it returns control back to the system.

Thus the typical race conditions that are caused by two threads of execution going at the same time do not happen in Javascript.

This includes all forms of Javascript events including: user events (mouse, keys, etc..), timer events, network events (ajax callbacks), etc...

The only place you can actually do multi-threading in Javascript is with the HTML5 Web Workers or Worker Threads (in node.js), but they are very isolated from regular javascript (they can only communicate with regular javascript via message passing) and cannot manipulate the DOM at all and must have their own scripts and namespace, etc...


While I would not technically call this a race condition, there are situations in Javascript because of some of its asynchronous operations where you may have two or more asynchronous operations in flight at the same time (not actually executing Javascript, but the underlying asynchronous operation is running native code at the same time) and it may be unpredictable when each operation will complete relative to the others. This creates an uncertainty of timing which (if the relative timing of the operations is important to your code) creates something you have to manually code for. You may need to sequence the operations so one runs and you literally wait for it to complete before starting the next one. Or, you may start all three operations and then have some code that collects all three results and when they are all ready, then your code proceeds.

In modern Javascript, promises are generally used to manage these types of asynchronous operations.

So, if you had three asynchronous operations that each return a promise (like reading from a database, fetching a request from another server, etc...), you could manually sequence then like this:

a().then(b).then(c).then(result => {
    // result here
}).catch(err => {
    // error here
});

Or, if you wanted them all to run together (all in flight at the same time) and just know when they were all done, you could do:

Promise.all([a(), b(), c()])..then(results => {
    // results here
}).catch(err => {
    // error here
});

While I would not call these race conditions, they are in the same general family of designing your code to control indeterminate sequencing.


There is one special case that can occur in some situations in the browser. It's not really a race condition, but if you're using lots of global variables with temporary state, it could be something to be aware of. When your own code causes another event to occur, the browser will sometimes call that event handler synchronously rather than waiting until the current thread of execution is done. An example of this is:

  1. click
  2. the click event handler changes focus to another field
  3. that other field has an event handler for onfocus
  4. browser calls the onfocus event handler immediately
  5. onfocus event handler runs
  6. the rest of the click event handler runs (after the .focus() call)

This isn't technically a race condition because it's 100% known when the onfocus event handler will execute (during the .focus() call). But, it can create a situation where one event handler runs while another is in the middle of execution.

jfriend00
  • 683,504
  • 96
  • 985
  • 979
  • "Any individual thread of execution" <--- what's that? Is it a single event loop iteration? Or is it a single event handler execution? – Vanuan Dec 02 '17 at 03:39
  • 1
    You need to define what race condition is. Race condition has nothing to do with threads. – Vanuan Dec 02 '17 at 04:06
  • @Vanuan - Because there's only one piece of Javascript running at any given time, there are none of the typical race conditions that one sees with threads where two threads try to access the same variable or the timing of when one thread accesses it vs. another is entirely unpredictable. Since there's only one piece of Javascript running at a time, that type of race condition simply can't happen. If you want to ask about a different type of race condition, then please explain what YOU are asking about because nobody else in this question is apparently asking about that. – jfriend00 Dec 02 '17 at 06:00
  • @Vanuan - Here's [one definition of a race condition](https://en.wikipedia.org/wiki/Race_condition) (obviously pay attention to the part about race conditions in software). And, also [What is a Race Condition](https://stackoverflow.com/questions/34510/what-is-a-race-condition). – jfriend00 Dec 02 '17 at 06:06
  • @Vanuan - Also, why are you dredging this up from over 6 years ago? – jfriend00 Dec 02 '17 at 06:19
  • Well, I find it typical practice for developers of a modern Redux application to dispatch multiple AJAX promises without thinking too much about synchronization. Even though there's no "typical" race condition and all variable changes are atomic, the Redux state change is not atomic. I.e. there can be multiple points of time where the state is inconsistent. The most common example is global "loading" state. You have to use counters to make sure there's no AJAX in progress. – Vanuan Dec 03 '17 at 21:36
  • "the timing of when one thread accesses [some global state] vs. another is entirely unpredictable." But it is upredictable with ajax requests, isn't it? When you create multiple promises and you don't know which of them will be resolved first. So you don't know which "thread" will access some global state first and which the second. You only know that there are no volatile variables. This is what I call "the race condition". – Vanuan Dec 03 '17 at 21:41
  • What you call a "typical" race condition is that the condition might became false after you verified that condition to be true. In this case we treat this condition to be volatile and the section that operates on this - "critical section", so we need a special "transaction variable" to ensure that this operation is atomic. This is not a type of race condition that exist in JavaScript, but there are others. – Vanuan Dec 03 '17 at 21:54
16

JavaScript is single threaded. There are no race conditions.

When there is no more code to execute at your current "instruction pointer", the "thread" "passes the baton", and a queued window.setTimeout or event handler may execute its code.

You will get better understanding for Javascript's single-threading approach reading node.js's design ideas.

Further reading: Why doesn't JavaScript support multithreading?

Community
  • 1
  • 1
Kijewski
  • 25,517
  • 12
  • 101
  • 143
  • 1
    This answer is not the full story. Please read my answer below: http://stackoverflow.com/a/12799287/1040124 – Jens Jan 30 '16 at 17:12
  • 1
    @Jens, incoming events won't preempt the current code, but you are right that there are races which callback will fire next. – Kijewski Jan 30 '16 at 21:50
  • How about [this race condition](https://stackoverflow.com/questions/44337989/actual-race-condition-in-javascript-with-assignment-operator)? – Dan Dascalescu Jun 02 '17 at 22:00
  • @DanDascalescu, the standard says that the LHS of an augmented assignment is evaluated first, then the RHS. Because the RHS contains an `await` expression, the current "thread" yields. This could be called a data race, but I would rather call it a misunderstanding of the standard. Simply use a temporary variable and everything will work fine. – Kijewski Jun 02 '17 at 22:07
  • @Ben, it is not. 1) A single worker only executes on expression at a time. 2) There is no involuntary yielding. 3) You cannot modify the data of another worker. → So there are no data races. – Kijewski Mar 20 '19 at 14:32
  • Sorry, but this is plain wrong. Every non-trivial language has race conditions. Javascript has an event loop and thus exhibits race conditions with async computations. –  Oct 30 '20 at 09:37
12

It is important to note that you may still experience race conditions if you eg. use multiple async XMLHttpRequest. Where the order of returned responses is not defined (that is responses may not come back in the same order they were send). Here the output depends on the sequence or timing of other uncontrollable events (server latency etc.). This is a race condition in a nutshell.

So even using a single event queue (like in JavaScript) does not prevent events coming in uncontrollable order and your code should take care of this.

Vanuan
  • 31,770
  • 10
  • 98
  • 102
Jens
  • 1,599
  • 14
  • 33
  • 2
    This is not a race condition, it's just how asynchronous code works. If you give a box to Bob and say "when you open this box, hand it back to me", then immediately turn to Sue and tell her the same thing, who do you get the box back from first? It's dependent on how long they take to open the box, and is outside of your control. Likewise, an AJAX request works the same way; you can't assume that they'll complete in any specific order because you're not ordering them, you're merely saying "when the request completes, run this callback function". – Daniel T. Feb 25 '17 at 00:44
  • 8
    Race condition doesn't have to occur with threads, it is simply a way saying you have 2 or more async functions competing for the same resource and you don't know who will win and what the final state of the execution be. This is why what @Jens described is correct – Slava Shpitalny Feb 25 '17 at 08:58
2

Sure you need. It happens all the time:

<button onClick=function() {
  const el = document.getElementById("view");
  fetch('/some/api').then((data) => {
    el.innerHTML = JSON.stringify(data);
  })
}>Button 1</button>

<button onClick=function() {
  const el = document.getElementById("view");
  fetch('/some/other/api').then((data) => {
    el.innerHTML = JSON.stringify(data);
  })

}>Button 2</button>

Some people don't view it as a race condition.

But it really is.

Race condition is broadly defined as "the behavior of an electronic, software, or other system where the output is dependent on the sequence or timing of other uncontrollable events".

If user clicks these 2 buttons in a brief period, the output is not guaranteed to depend of the order of clicking. It depends on which api request will be resolved sooner. Moreover, the DOM element you're referencing can be removed by some other event (like changing route).

You can mitigate this race condition by disabling button or showing some spinner when loading operation in progress, but that's cheating. You should have some mutex/counter/semaphore at the code level to control your asynchronous flow.

To adapt it to your question, it depends on what "block()" is. If it's a synchronous function, you don't need to worry. But if it's asynchronous, you have to worry:

  function block() {
    window.blockInProgress = true;
    // some asynchronous code
    return new Promise(/* window.blockInProgress = false */);
  }

  if(!window.blockInProgress) {
    block();
  } else {
    window.flashReady = block;
  }

This code makes sense you want to prevent block from being called multiple times. But if you don't care, or the "block" is synchronous, you shouldn't worry. If you're worried about that a global variable value can change when you're checking it, you shouldn't be worried, it's guaranteed to not change unless you call some asynchronous function.

A more practical example. Consider we want to cache AJAX requests.

 fetchCached(params) {
   if(!dataInCache()) {
     return fetch(params).then(data => putToCache(data));
   } else {
     return getFromCache();
   }
 }

So happens if we call this code multiple times? We don't know which data will return first, so we don't know which data will be cached. The first 2 times it will return fresh data, but the 3rd time we don't know the shape of response to be returned.

Vanuan
  • 31,770
  • 10
  • 98
  • 102
  • This is not a race condition, it's a misunderstanding of how asynchronous code works. Using a more standard example, imagine if you asked two people to go to their desks, grab a pen, and come back and put it on your desk, who will put their pen on your desk first? You don't know because you don't control them. Perhaps one guy ran and the other walked. Maybe one guy uses the bathroom first. This is no different than creating two buttons that perform network requests. Which completes first? You don't know because you don't control the network response times. – Daniel T. Dec 02 '17 at 04:46
  • The misunderstanding is in expecting that if button 1 is clicked and then button 2 is clicked shortly afterwards, then the first response should be returned followed by the second. This is no different than asking person 1 first to get me a pen, then asking person 2 to get me a pen shortly afterwards, and expecting person 1 to get me a pen first. – Daniel T. Dec 02 '17 at 04:47
  • Let's make a small change to your example. One person has a red pen, another person has a blue pen. You ask these two people to place a pen to your desk, but if there's already a pen you want it to be replaced. – Vanuan Dec 03 '17 at 22:07
  • I say that the race condition is when you don't control which pen will end up on your desk, blue or red. You say that the race condition is when you might end up with 2 pens on your desk. – Vanuan Dec 03 '17 at 22:09
  • The type of race condition you think to be the only one is called "time of check to time of use": https://en.wikipedia.org/wiki/Time_of_check_to_time_of_use , but there are other ones. – Vanuan Dec 03 '17 at 22:20
  • 1
    I ask person A to put a red pen on my desk, replacing any that might already exist. I ask person B to put a blue pen on my desk, replacing that any might already exist. Both people have to go back to their desks to grab the pen first. Which color pen will be on my desk after both people come back? The obvious answer is "whoever comes back last", but that can only determined after the fact (or in programming terms, at runtime). This is no different than creating two buttons and trying to determine which button is clicked first; you don't control that, the user does. – Daniel T. Dec 05 '17 at 02:35
  • 1
    A race condition doesn't exist because you don't control which pen ends up on your desk. That stems from an incorrect premise: that you can guarantee the speed at which both people complete the task you gave them. Both people completed the task exactly as given to them, independently of each other, at their own speed. Likewise, if you initiate two network requests that run independently and resolve independently, but modifies one resource, this isn't a race condition. It's you assuming that request A will complete before request B simply because you initiated request A first. – Daniel T. Dec 05 '17 at 02:40
  • 1
    If this is still confusing, think about it this way: a race condition is where the output is unexpected given the same two inputs. Conversely, this must mean that there was an expectation that was unmet. What is the expectation in the case that two network requests modify the contents of the same div? Do you expect that request B's contents will be in the div because you called it second? If the answer is "I don't know because I don't know when the network requests complete", then there is no unexpected output because you have no initial expectation. – Daniel T. Dec 05 '17 at 02:48
  • Huh? If I think about it this way, there's nothing unexpected. It's just how works. – Vanuan Dec 06 '17 at 03:15
  • Programming is not about expectation. It's about intention. I.e. programmer intended that user will wait for request to finish after clicking button 1 or 2. But he failed to ensure that by putting mutexes in place. Similarly, programmer intended that another thread will wait before programmer is done with a critical section, but failed to ensure that. I don't see the difference here. – Vanuan Dec 06 '17 at 03:31
  • Wikipedia: "race condition becomes a bug when events do not happen in the order the programmer intended. The term originates with the idea of two signals racing each other to influence the output first." Are you saying network requests are not two signals racing each other? – Vanuan Dec 06 '17 at 03:38
  • Are you saying that programmer doesn't intend a particular order of network requests completion? So you're basically saying that race condition is a race condition only when it's a bug? Or when it's a bug that is not obvious? – Vanuan Dec 06 '17 at 03:44
  • "Race condition becomes a bug when events do not happen in the order the programmer intended" If I send two simultaneous network requests, my **intention** might be for A to finish before B, and my **expectation** is also the same. However, both are incorrect. Using the pencil analogy, I **intended** for person A to return before person B, but I did not explicitly tell them to return to me in any order. Hence, if person B returns the pencil before person A, this is not a race condition, it's a programming error because I did not program it properly. – Daniel T. Dec 06 '17 at 07:31
  • 1
    This is no different than **intending** to set a = 4; a= 3, but I set it to a = 3; a = 4, and calling it a "race condition" because "events do not happen in the order that the programmer intended". As for the **expectation**, this is also wrong. I do not have control over either person's actions, and thus it is logically senseless to expect that one person will return to me before the other. I can only do something after both return to me, for example, selecting one of the two, or rejecting the second pencil because I already have a first. – Daniel T. Dec 06 '17 at 07:36
  • 1
    What you're saying is that the act of sending two people out at the same time and not knowing when they will return is a race condition. This is logically senseless. You're also saying that because you don't know what will be on your desk in the end, this is also a race condition, but that's ignoring the fact that you **know** when someone returns, and you can act on that fact (a.k.a. the callback function). If I tell two people to grab me a pen, I can decide what I want to do once I have a pen in my hand. – Daniel T. Dec 06 '17 at 07:40
  • 1
    I can turn down the second pen because I already have a first (a.k.a. contents of DOM has already been filled by the first request, don't do anything when the second request completes). I can call the second person and tell them to stop because I already have a pen (a.k.a. cancel the other request once one of the requests completes). Or I can sit there and wait for both people to come back and say "well I didn't know what order you guys were going to come back to me in, and this wasn't my intention, so therefore race condition" :). – Daniel T. Dec 06 '17 at 07:43
  • 1
    In any case, feel free to look up other SO questions related to JS and race conditions (spoiler alert: they all say JS has no race conditions). There are also multiple blog posts that you can look up, like this one: https://blog.raananweber.com/2015/06/17/no-there-are-no-race-conditions-in-javascript/ – Daniel T. Dec 06 '17 at 07:45
0

Yes, of course there are race conditions in Javascript. It is based on the event loop model and hence exhibits race conditions for async computations. The following program will either log 10 or 16 depending on whether incHead or sqrHead is completed first:

const rand = () => Math.round(Math.random() * 100);

const incHead = xs => new Promise((res, rej) =>
  setTimeout(ys => {
    ys[0] = ys[0] + 1;
    res(ys);
  }, rand(), xs));

const sqrHead = xs => new Promise((res, rej) =>
  setTimeout(ys => {
    ys[0] = ys[0] * ys[0];
    res(ys);
  }, rand(), xs))

const state = [3];

const foo = incHead(state);

const bar = sqrHead(state);

Promise.all([foo, bar])
  .then(_ => console.log(state));