I am a bit of a newby to JS and was just wondering if I would need to make an object mutually exclusive if I plan to write data to it from a bunch of different async functions? To give a more concrete example, I am trying to parse some data related to sporting events from a bunch of different websites and want to write specific information related to said sites in a central object that holds general information about a sporting event as well as more specific event data that I pull from each individual site. Ideally all of these operations would be async from one another to improve performance.
Thanks in advance!
Nodejs runs your Javascript in a single thread. So, two pieces of your Javascript will not be actively running at the exact same time. Because of this, you won't have issues with two pieces of your Javascript both trying to access an object at the exact same time the way you might in something like multi-threaded C++ code. This is true no matter what the source of your Javascript is, whether it's in an async function or not.
Even if you use WorkerThreads, they don't provide simultaneous access to the same variables (each WorkerThread has it's own variable space) and you would communicate value changes via messaging which runs through the event loop, thus coordinating access with your other code. At the lowest level of WorkerThreads, there is a SharedArrayBuffer data type that does allow access between threads and you would have to use Atomics or something similar to protect your access. But, this is a very specific situation and a very specific datatype only.
That said, there are still fairly simple ways you can have race conditions in Javascript and some of those involve async/await For example, if you have some shared resource you're accessing in your code (a resource that is accessed by other parts of your code), then anytime you hit an await or a .then() callback in your code, that is place where the interpreter goes back to the promiseQueue or the event loop and can run other code while waiting for that promise to resolve/reject. If that other code can also modify this shared resource and that's a problem for the first code, then you may have a race condition.
So, if you just do something like this:
let someValue = someSharedObj.prop;
let delta = await someFunction();
someSharedObj.prop = someValue + delta;
You have to know that someSharedObj may be accessed by other code while you are at the await and this could be a race condition where this code may overwrite a change that some other code did while you were sitting at the await. This could be rewritten as such:
let delta = await someFunction();
let someValue = someSharedObj.prop;
someSharedObj.prop = someValue + delta;
And the race condition no longer exists because it's entirely synchronous code between the time you get the prop, add a value to it and write it back. No other Javascript can run between those last two statement. If other code happens to run during the await, that does not cause a problem for updating the someSharedObj.
It's a bit hard to advise generically on how to avoid these. With a database, the database will typically have atomic operations you can use to avoid race conditions or transactions or locks. For example, most databases will have an atomic way of incrementing or decrementing a value without having to asynchronously get the value, increment and asynchronously write it back (which is vulnerable to race conditions).
For your own shared data, avoiding race conditions generally involves not holding data across an await or .then() callback.