| ▲ | rstuart4133 3 days ago | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Async is a Javascript hack that inexplicably got ported to other languages that didn't need it. The issue arose because Javascript didn't have threads, and processing events from the DOM is naturally event driven. To be fair, it's a rare person who can deal with the concurrency issues threads introduce, but the separate stacks threads provide a huge boon. They allow you to turn event driven code into sequential code.
becomes:
The latter is easy to read linear sequence of code that keeps all the concerns in one place, the former rapidly becomes a huge entangled mess of event processing functions.The history of Javascript described in the article is just a series of attempts to replace the horror of event driven code with something that looks like the sequential code found in a normal program. At any step in that sequence, the language could have introduced green threads and the job would have been done. And it would have been done without new syntax and without function colouring. But if you keep refining the original hacks they were using in the early days and don't the somewhat drastic stop of introducing a new concept to solve the problem (separate stacks), you end up where they did - at async and await. Mind you, async and await to create a separate stack of sorts - but it's implemented as a chain objects malloc'ed on the heap instead the much more efficient stack structure. I can see how the javascript community fell into that trap - it's the boiling frog scenario. But Python? Python already had threads - and had the examples of Go and Erlang to show how well then worked compared to async / await. And as for Rust - that's beyond inexplicable. Rust has green threads in the early days and abandoned them in favour of async / await. Granted the original green thread implementation needed a bit of refinement - making every low level choose between event driven and blocking on every invocation was a mistake. Rust now has a green thread implementation that fixes that mistake, which demonstrates it wasn't that hard to do. Yet they didn't do it at the time. It sounds like Zig with its pluggable I/O interface finally got it right - they injected I/O as a dependency injected at compile time. No "coloured" async keywords and compiler monomorphises the right code. Every library using I/O only has to be written once - what a novel concept! It's a pity it didn't happen in Rust. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ▲ | rafaelmn 8 hours ago | parent | next [-] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
async/await came out of C# (well at least the JS version of it). There are a bunch of use cases for it outside of implementing concurrency in a single threaded runtime. Pretty much every GUI toolkit I've ever used was single threaded event loop/GUI updates. Green threads are a very controversial design choice that even JVM backed out of. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ▲ | captainmuon 8 hours ago | parent | prev | next [-] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
JavaScript got async in 2017, Python in 2015, and C# in 2012. Python actually had a version of it in 2008 with Twisted's @inlineCallbacks decorator - you used yield instead of await, but the semantics were basically the same. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ▲ | aw1621107 7 hours ago | parent | prev | next [-] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
> And as for Rust - that's beyond inexplicable. Rust has green threads in the early days and abandoned them in favour of async / await. There was a fair bit of time between the two, to the point I'm not sure the latter can be called much of a strong motivation for the former. Green threads were removed pre-1.0 by the end of 2014 [0], while work on async/await proper started around 2017/2018 [1]. In addition, I think the decision to remove green threads might be less inexplicable than you might otherwise expect if you consider how Rust's chosen niche changed pre-1.0. Off the top of my head no obligatory runtime and no FFI/embeddability penalties are the big ones. > Rust now has a green thread implementation that fixes that mistake As part of the runtime/stdlib or as a third-party library? | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ▲ | senfiaj 6 hours ago | parent | prev | next [-] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
> Python already had threads But for a long time (I think even till today despite that there is as an optional free-threaded build) CPython used Global Interpreter Lock (GIL) which paradoxically makes the programs run slower when more threads are used. It's a bad idea to allow to share all the data structure across threads in high level safe programming languages. JS's solution is much better, it has worker threads with message passing mechanisms (copying data with structuredClone) and shared array buffers (plain integer arrays) with atomic operation support. This is one of the reasons why JavaScript hasn't suffered the performance penalty as much as Python has. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ▲ | josephg 5 hours ago | parent | prev | next [-] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
> At any step in that sequence, the language could have introduced green threads and the job would have been done. The job wouldn’t have been done. They would have needed threads. And mutexes. And spin locks. And atomics. And semaphores. And message queues. And - in my opinion - the result would have been a much worse language. Multithreaded code is often much harder to reason about than async code, because threads can interleave executions and threads can be preempted anywhere. Async - on the other hand - makes context switching explicit. Because JS is fundamentally single threaded, straight code (without any awaits) is guaranteed to run uninterrupted by other concurrent tasks. So you don’t need mutexes, semaphores or atomics. And no need to worry about almost all the threading bugs you get if you aren’t really careful with that stuff. (Or all the performance pitfalls, of which there are many.) Just thinking about mutexes and semaphores gives me cold sweats. I’m glad JS went with async await. It works extremely well. Once you get it, it’s very easy to reason about. Much easier than threads. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ▲ | kibwen 6 hours ago | parent | prev | next [-] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
> And as for Rust - that's beyond inexplicable. No, you appear to have no idea what you're talking about here. Rust abandoned green threads for good reason, and no, the problems were not minor but fundamental, and had to do with C interoperability, which Go sacrifices upon the altar (which is a fine choice to make in the context of Go, but not in the context of Rust). And no, Rust does not today have a green thread implementation. Furthermore, Rust's async design is dramatically different from Javascript, while it certainly supports typical back-end networking uses it's designed to be suitable for embedded contexts/freestanding contexts to enable concurrency even on systems where threads do not exist, of which the Embassy executor is a realization: https://embassy.dev/ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ▲ | 01HNNWZ0MV43FF 8 hours ago | parent | prev | next [-] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
What if process_the_character takes multiple seconds waiting on a network request? | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ▲ | Ygg2 8 hours ago | parent | prev [-] | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
> Rust has green threads in the early days and abandoned them in favour of async / await. Granted the original green thread implementation needed a bit of refinement - making every low level choose between event driven and blocking on every invocation was a mistake. That's a mischaraterization. They were abandoned because having green threads introduces non-trivial runtime. It means Rust can't run on egzotic architectures. > It sounds like Zig with its pluggable I/O interface finally got it right That remains to be seen. It looks good, with emphasis on looks. Who knows what interesting design constraints and limitation that entails. Looking at comptime, which is touted as Zig's mega feature, it does come at expense of a more strictly typed system. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||