Knowing if a function will yield the thread is actually extremely relevant knowledge you want available.
Knowing if a function will yield the thread is actually extremely relevant knowledge you want available.
You can do that. If you don't await an async call, you have a future object that you can handle however you want.
The sync code might be running in an async context. Your async context might only have one thread. The task you're waiting for can never start because the thread is waiting for it to finish. Boom, you're deadlocked.
Async/await runtimes will handle this because awaiting frees the thread. So, the obvious thing to do is to await but then it gets blamed for being viral.
Obviously busy waiting in a single threaded sync context will also explode tho...
Async looks great in a blog post full of clean examples. It... kinda doesn't in four year old code written by people who've left the project.
Zig's colorless async was purely solving the unergonomic calling convention, at the cost of knowing if a function is async or not (compiler decides, does not give any hints and if you get it wrong then that's UB).
Arguably the main problem with async is that it is unergonomic. You always have to act like there were 2 types of functions, while, in practice, these 2 types are almost always self-evident and you can treat sync and async functions the same.
When you know what functions and blocks are synchronous, you know the thread will not be yielded. If you direct async tasks to run on a single thread, you know they will never run concurrently. These together mean you can use that pattern to get lock free critical sections. You don't need to write thread-safe data structures.
If a function can yield implicitly, how do you have the control you need to pull this off?
It's a really common pattern in GUI dev so how does Zig handle that?
Constness is infectious down the stack (the callee of a const function must be const) while asyncness is infectious up the stack (the caller of an async function must be async). So you can gradually add constness to subsections of a codebase while refactoring, only touching those local parts of the codebase. As opposed to async, where adding a single call to an async function requires you to touch all functions back up to main
Javascript's async as of ten years ago just happened to be an especially annoying implementation of a specific effect.
When is this relevant beyond pleasing the compiler/runtime? I work in C# and JS and I could not care less. Give me proper green threads and don't bother with async.
If yields are implicit, you don't have enough control to really pull that off.
Maybe it's possible but I haven't seen a popular green threaded UI framework that let's you run tasks in background threads implicitly. If I need to call a bunch of code to explicitly parcel background work, that just ends up being async/await with less sugar.
Node is one place where async-await has zero counter arguments and every alternative is strictly worse.
Node is not a web page, so no reason to limit it to the same patterns.
Then, the next issue would be thread safety. But that could be treated as a separate problem.
The motivation for node was that users wanted to use JavaScript on the server.
What do you mean? A JS runtime can't do anything useful on its own, it can't read files, it can't load dependencies because it doesn't know anything about "node_modules", it can't open sockets or talk to the world in any other way - that's what Node.js provides.
> I would claim it was the only low-effort model available to them and therefore not motivation.
It was a headline feature when it released.
https://web.archive.org/web/20100901081015/https://nodejs.or...
In the above link Node could be described as a Chrome V8 distribution with modules enabling building a web server.
Adding threading to a non-threaded scripting runtime is another ball game.
The point is that Node was forced into this model by V8 limitations, then sold it as an advantage, however, it is only one way to solve the problem with its own trade-offs and you have to look at the specific use case you are looking at to see if it is really the best solution for your use case.
Yes, obviously, that's what NodeJS does. But you can't "just use the V8 runtime as-is if you're doing async IO", it doesn't have those facilities at all.
Async IO wasn't just "sold as an advantage", it is an advantage. Websockets were gaining popularity around that time and async IO is a natural fit for that.
You would have to change the language and boil the ocean to make the runtime support multiple threads (properly).
But why? Just to end up with the inferior thread-per-request runtime (which by the way, still needs to support async because it's part of the language), that requires developers to write JS which is incompatible with browser JS, which would've eliminated most of the synergy between the two?
I really don't understand what you're going for here. I don't see a single advantage here.
If you don't have many threads, OS threads are okay as well. It is all about memory and scheduling overhead.
But that is just my opinion. You are welcome to have a different opinion.
Node dictates that when faced with an async function the result is that I must either implement async myself so I can do await or go into callback rabbit holes by doing .then(). If the function author is nice, they will give me both async and sync versions: readFile() and readFileSync(). But that sucks.
The alternative would be that 1) the decision to go async were mine; 2) the language supports my decision with syntax/semantics.
Ie. if I call the one and only fs.readFile() and want to block I would then do
sync fs.readFile()
Node would take care of performing a nice synchronous call that is beneficial to its event-loop logic and callback pyramid. End of the story. And not some JS an implementation such as deasync [1] but in core Node.No, just callbacks and event handlers (and an interface like select/poll/epoll/kqueue for the OS primitives on which you need to wait). People were writing threadless non-blocking code back in the 80's, and while no one loved the paradigm it was IMHO less bad than the mess we've created trying to avoid it.
One of the problems I'm trying to point out is that we're so far down the rabbit hole in this madness that we've forgotten the problems we're actually trying to solve. And in particular we've forgotten that they weren't that hard to begin with.
I don’t have anything against async, I see the value of event-oriented “concurrency”, but the complaint that async is a poison pill is valid, because the use of async fundamentally changes the execution model to co-operative multitasking, with possible runtime issues.
If a language chooses async, I wish they’d just bite the bullet and make it obvious that it’s a different language / execution model than the sync version.
Calling sync code from async is fine in and of itself, but once you're in a problem space where you care about async, you probably also care about task starvation. So naively, you might try to throw yeilds around the code base.
And your conclusion is you want the language to be explicit when you're async....so function coloring, then?