6 ms·
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 pro
by rstuart4133 6mo 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.
window.on_keydown(foo);
// Somewhere far away
function foo(char_event) { process_the_character(char_event.key_pressed) };
becomes:
while (char = read())
process_the_character(char);
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.
- 01HNNWZ0MV43FF 6mo agoWhat if process_the_character takes multiple seconds waiting on a network request?
- rstuart4133 6mo agoThe key to understanding is green threads are semantically identical to async - just formulated a little differently. The code I gave left out a lot of details. This: while (char = read()) process_the_character(char); It may look more like this: element.onkeydown = window.greenThread(processKeyEvents, ...); ... function processKeyEvents(green_thread, ...) { while (event = green_thread.yield(...)) { /* process the key event */ var foo = call_some_function_that_blocks(...); /* more processing */ } } You can only call a function that blocks (like `green_thread.yield()`) from within a green thread. Attempt it outside of a green thread and they throw an exception. Code is effectively still coloured, because a call that can block can't be called from a context that doesn't allow block (like DOM events). async makes this colouring obvious but it comes at the expense of new keywords, new concepts like promises and futures, and manually creating the stack using linked object on the heap. A real stack is far faster than allocating stuff on the heap. But to repeat the key observation above: async and green threads are semantically near identical, it is only the implementation is different. One selling point used for async is it makes blocking calls obvious, and that would somehow avoid bugs. The claim (willfully?) ignored that green threads (aka, cooperative multiasking) were the earliest form of multitasking - they have been around for over 50 years at this point. They were never a source of the sort of bugs they claimed would happen.
- rafaelmn 6mo agoasync/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.
- Ygg2 6mo ago