4 ms·
This is basically content free, and what content is there is mostly incorrect. Yes - shared data can be modified in between events on the event loop. No - thi
by horsawlarway 5y ago
This is basically content free, and what content is there is mostly incorrect.
Yes - shared data can be modified in between events on the event loop. No - this is not unexpected, or really a problem. Events are essentially atomic operations, and reasoning about them is much easier than equivalent reasoning in paradigms that allow pre-empting at any point (ex - threads).
And that single paragraph has more details than the entire article, since "you can find it elsewhere" according to the source...
- klabb3 5y agoConcurrent programming is much more difficult and also come in numerous paradigms/flavors which unfortunately lacks an agreed-upon taxonomy and terminology, so I'd forgive the author for making some confusing statements. Focusing on the "event loop" is a conceptual mistake imo, since it associates heavily to the (traditional) callback-oriented concurrency in JS. With callbacks, it is expected that shared state can be changed between registering and executing the callback. However, with async, this line is blurred enough to mess with the programmer's intuition (this is of course subjective, but I've seen enough to know that folks are confused). Async functions are (mostly) syntactic sugar for callbacks, and as such each await-point is where things can change - yet people don't expect it. When I help folks get up to speed on async, the first thing I call out is that the await points have this important semantic property. Any use of mutable state outside an async function is a ticking time bomb. The other thing that I disagree with is the "logical mutex" synchronization in js. Async iterators would be preferable in most real world situations, but they're not even mentioned. Async is a blessing if it's used sanely, but a curse if it's not. It doesn't magically make problems go away, but it can make your code more concise.
- xg15 5y agoI found it somewhat interesting psychologically how much of a paradigm shift async/await seems to have caused, even though it's really just syntactical sugar. It changed absolutely zero about JavaScript's processing model - but because it "looks" like traditional multithreaded code, people seemed to start thinking about it like that, suddenly asking about synchronization, locks, etc. You could have asked all those questions already during callback-hell times, but I don't remember reading any blogs about that stuff back then.
- klabb3 5y agoAgreed. People conflate concurrency with parallelism a lot, so I guess people see async, squint, "hey this is just like threads", and then start grabbing for locks. It appears to me that Javascript has painted itself into a corner where it needs to maintain 3 concurrency models for the forseeable future (callbacks, async, service workers), which is unfortunate for ecosystem interop. It's a shame because JS had this quite amazing property for such an old language that its concurrency model was simple, approachable and performant (within that single thread). Now that people build more general purpose stuff with it, those days are fading.
- mikojan 5y agoConcurrency in JavaScript is simple because there are usually just 1 or 2 event sources. Event sources that are mostly idling, too. It has nothing to do with threads v. no threads.
- horsawlarway 5y agoNo - concurrency is simple because the default state of running code is atomic. Variables CAN NOT change out from under you unless you explicitly place code in a callback, or await it. This is utterly untrue in threaded code, where ANY line that is not explicitly guarded or explicitly an atomic operation, might have shared data change out from underneath it. There are usually several events sources - just in a normal application - Network calls - Mouse actions - Keyboard actions - Timers - awaits/promises/callbacks - Service workers - mutation observers - postMessage events - etc... Are all viable sources of events. Some of them will trigger events incredibly often (ex - a handler registered to mouseMove or Scroll) - it has nothing to do with limited event sources, and everything to do with how the code executing the handlers is guaranteed to be atomic (unless it adds more events - such as a callback or an await)
- xg15 5y ago> Yes - shared data can be modified in between events on the event loop. I think the problem is more that data can be modified between an event occurring and the handlers for that very event being called. I think we often conflate the time an event occurs with the time the event's handler is executed - when in reality, there can be an arbitrary, nontrivial delay between them due to queuing. This means that any state invariants that an event might establish can already be long broken by the time your handler is called. E.g., suppose you write database code in NodeJS and query an external SQL database. Your NodeJS process is single-threaded, uses only a single database connection and there is no other process interacting with the database either. Then the following code might seem safe: let flaggedThings = await db.query("SELECT * FROM things WHERE flag = true"); process(flaggedThings) // assume each thing has the flag set to true However, it's not. After the DB query returned, there will be some internal call to Promise.resolve() which will advance the script past the await. However, the event of the DB query returning will not immediately cause this call - it will only cause a task for the call to be added to the event queue. But at the time the event loop dequeues and executes the task, other tasks, which were dequeued earlier, might already have changed the DB state. Therefore, the resultset may already be stale at the moment the script continues after the await.