Defining async is hard. And I'm writing this as one of the many people who designed async in JavaScript.
I don't quite agree with the definition in this post: just because it's async doesn't mean that it's correct. You can get all sorts of user-land race conditions with async code, whether it uses `async`/`await` (in languages that need/support it) or not.
My latest formulation (and I think that it still needs work) is that async means that the code is explicitly structured for concurrency.
The important thing to me is to distinguish between the abstract concept of asynchronism and how it can be implemented, and by the latter I mean both at the abstract level of a programming language and by technical coordination means in a machine. For the abstract concept at the highest level, well, it is just the dual of synchronism: two (or more) parties that somehow need to work together (i.e., one has a dependency on the other in a way that certain things need to happen before another one can continue) are not synchronized, meaning that it is not known or not defined when the things that need to happen after something else will be done. Seen that way, this definition is not hard. The hard thing can be the abstract means designed in a language: the amount of cognitive effort it takes in order to comprehended them and/or use them (in an fault-free way).
Largely agreed: async means that the code is structured in such a manner that the only way to be certain that a task is complete is to perform some rendez-vous. By extension, it covers mechanisms to make this happen in your code.
I am not deep into the matter but I would have given the answer: Async code is making code that would have been blocking non-blocking in a manner other stuff can still happen while it is being completed.
Since I work a lot in embedded loops where long running blocking snippets could literally break your I/O, lead to visible/audible dropouts etc. this would be the obvious answer.
But that's the thing: async, by itself, doesn't guarantee that anything is non-blocking. For your fiber (or any other kind of user-land abstraction) to be non-blocking, you MUST ensure that it doesn't perform any blocking call.
All async does is give you (some of) the tools to make code non-blocking.
Yeah, sure I mean in embedded-land any async snippet could perform any number of things, like firing a delay command that puts the whole processor to sleep.
This could potentially be avoided by clever enough compilers or runtimes, but I am not sure whether that would really be benefitial.
I am a fan of making things explicit, so the closer peoples idea of what aync is and what it isn't matches reality the better. Alternatively we should get the definition of what async should be clear first and then make the adjustment to the abstractions so they give us the guarantees people would natuarally assume come with that.
Yeah, I'm insisting because I recently reviewed a PR with async code calling blocking code, which made the entire exercise pointless. And that was from an experienced dev.
There used to be a few compilers that used static analysis to predict the cost of a call (where the cost of I/O was effectively considered infinite) and in which you could enforce that a branch only had a budget of N. Modern architectures tend to mess up with any finite value of N, but you could fairly easily adapt such techniques to detect unbounded values.
The entire point might be to offload the blocking call and do something else while it's blocking.
There's a style of "asynchronous programming" where everything is designed to be non-blocking and there can be asynchronous programming with blocking code. In fact the first style can be emulated by offloading every blocking call to a different thread/greenthread/fiber and that's basically what's happening under the hood unless there is some fundamental support for non-blocking at the lower levels (sometimes all the way down to the hardware).
You can run blocking code asynchronously from other code. I think the parent means something like if you have a blocking operation (function call e.g.) on a thread you can create another thread and run that blocking operation there, magically transforming a blocking call to something that looks like a non-blocking call (thus allowing other code to do something else instead of waiting)
If you look on the bright side, it looks like free-threading is approaching, and OCaml has demonstrated how, by removing the GIL and adding exactly one primitive, you can turn a powerful enough language into a concurrency/parallelism powerhouse with minimal user-visible changes!
it may be hard (it is) because it cannot be matched to one thing
the question is: is it useful to define async? or event loop? there must be tons of concepts i have no idea in the realm of physical chips that make true parallelism possible
i am totally fine with "user finger" and "quickies", job queues and blocking or non-blocking APIs
the finger symbolizes touch events and even mouse clicks and keyboard or general user initiated events, which i have to match to quickies which are very tiny (execution time) blocking(!) jobs that will be queued by the browser
to reach my goals, i prefer non-blocking APIs because i can discard some time consuming jobs to underlying systems and i jsut write a quicky for what i want (store data in indexed db) and what will happen if it succeeds or fails etc (different quickies)
sync, async do not really help me, of course i have to understand when others talk about it or i see or use (or my preferred AI coder) async, but it just means non-blocking API
but again, the async programming model is actually writing very much blocking quickies where the non-blocking nature is the small, atomic nature (execution-time-wise) of the blocking jobs I try to match to chaotic, non-deterministic events, triggered by fingers or browsers or whatever
I actually dont care, just hope that the browser code uses great concurrent models with cpp or rust or whatever and the device has multiple executions units (os threads) and the os scheduler does a great job, managing things whether there are more execution units or just 1 available
async for me a not well defined concept and even if it was somehow defined, i am not sure it would be useful to me
useful concepts are events, the blocking nature of jobs i write in js, what my functions see (closure i guess), what runs as a job if i use APIs and what runs as a different job after the events the browser triggers (ready, error whatever)
even the name callback was extremely confusing because i really thought back then that the code somehow stops there and waits for a callback... no, it runs to the end of that section and you really have to understand what other things run when it "calls back" and what that code sees
to be honest, i think it is a mess and genius at the same time... but understanding "async" or rather the model was really difficult because i just dont think this means anything
it is actually very simple to understand with different concepts like event, blocking job, job queue, non-blocking API
what i also find important to know what we are doing and what others do like browser code, os etc... it is a bit like a cpp code declares a concurrent model with a thrad but the os will decide... in js, we use non-blocking api which implicitly declares a probably concurrent model the browser or node or whatever should use and i am sure they always do
the most important thing is to keep your jobs quick, probably under 30-50ms and non-blocking API are great because your job just declares the intent and done, and languages like cpp, rust will declare the os that they want the actual task done concurrently so even if the os has one real physical thread, the UI will be responsive since the OS will switch between UI code execution and "real task" execution like some networking or database or whatever
but all an "async" programmer has to do is to create a great UX model and match events to quickies
I don't quite agree with the definition in this post: just because it's async doesn't mean that it's correct. You can get all sorts of user-land race conditions with async code, whether it uses `async`/`await` (in languages that need/support it) or not.
My latest formulation (and I think that it still needs work) is that async means that the code is explicitly structured for concurrency.
I wrote some more about the topic recently: https://yoric.github.io/post/quite-a-few-words-about-async/ .