for this reason i always optimize the shit out of all synchronous code before resorting to async. and if you do need to go async after that, you might be better off just offloading that optimized synchronous op to a worker thread (or to wasm) to not block the UI.
modern JS vms are insanely fast when you pay attention to GC pressure, mem allocation, monomorphism, and dont use accidentally quadratic algos. it's rare that i encounter a codebase that can't be sped up by some large multiple without ever resorting to an async/defer crutch.
I believe it should be the answer. If your computations are tolerably fast, then you could do it without async, but if they are not, then it is better to use preemptive multitasking for them. The overhead on the kernel scheduler will be small, because you don't start 10k of threads concurrently eating CPU time. Probably the overhead of starting a thread doesn't matter either with long tasks. As a bonus you could also do blocking i/o operations without thinking about blocking.
In my experience almost everything in the JS world is already async. User interactions are async, requests are async, almost all NodeJS APIs are async. To me having to add more async in JS is a tiny barrier compared to what I'm facing in other languages that feel more synchronous to me.
Since there is already so much async I also feel like debugging, profiling and error handling are all pleasantly solved problems in JS.
Offloading to workers is also async so while there are many valid benefits to be gained, avoiding async does not seem like one of them to me.
A lot of junior devs I've worked with don't understand that putting `async` in front of a function doesn't actually make it asynchronous.
of course it does. annotating any function with async makes it implicitly return a Promise, which fundamentally changes how all callers have to use it (and their caller's callers, etc.). you can't "just" make a function async and change nothing about how it was used previously.
https://jsfiddle.net/om3tj2rd/
async function foo() {
return 2;
}
console.log(foo());
thisi should clarify a bit, that this can still freeze your UI if foo() is expensive, since the microtask still runs on the same event loop. my point is that you cannot always throw async in front of a function and not change any other code.
It works in principle, but note that this really complicates your build process. In particular, if you're writing a library that other people will use as a dependency, there's really no good way to use workers at all without affecting how people bundle their code using your library.
What I find a pain is the uneven support for shared workers.
Was it before or after we started calling "man-hours" "story points"?
Parallel means that things execute at the same time (or at least appear to do so). Async means that you yield control to some kind of scheduler and get it back at a later point.
Barring workers, JavaScript actually guarantees that two pieces of code never execute at the same time. That's the run-to-completion semantics.
When async was introduced to JavaScript from two different angles (callbacks in Node, Promise then await in browsers), there was limited parallelism involved (typically running I/O in the background), but the async was meant to ensure that:
1. while a task was (almost explicitly) waiting, another one could run;
2. in the case of the browser, we could yield control from JavaScript often enough that the UI could run smoothly.
img.onload = function() {…}
img.src = “some url”
img.load(“some url”, function() {…})
The early JavaScript APIs use the first style, but the result is the same.Async code is scheduling different parts after each other. It's still running in a single thread (maybe on same core)
aaync essentially is non-blocking IO in many cases. It can also ahieldnsome logic running in a diffefent thread, hidden from that segment of code.
What's worse is I don't even know how you're supposed to know what the edge cases are without stumbling into them. You shouldn't have to know about APIs like document.hidden unless you specifically want to handle background tabs differently. They shouldn't leak into the regular event loop.
Yea targeting the web is nuts if you have other options!
I think that it's a combination of two things:
1. many webdevs just have no clue what a thread is, because generally, they don't need it, so it isn't taught;
2. most of the documentation you can find online was written by people (and now ChatGPT) who don't understand async, sprinkle the word randomly and tweak things until they seem to work.
As a consequence, webdevs learn that async is magic. Which is a shame, because the underlying model is almost simple (the fact that we have both micro-tasks and tasks complicates things a bit).
JavaScript doesn't have threads.
It looks like web workers is the way for JavaScript to do multi-threading.
Async has always been enough for what I need to do in the front end, as most of my long running processes are just calling a back end.
Edit to add: for context, I am a full stack developer and know what threads are... I just never have needed them in the browser.
On the other hand, the isolation guarantees are strong. There aren't really any footguns. Messaging is straightforward, works with a lot of data types and supports channels for inter-worker communication.
Both your points, 1 + 2, are variations on that.
The right answer is the problem is the language design.
I do a lot of work on a platform that has doubled in user base yearly since its inception, for about a decade (though I think we are at an inflection point now or soon), and it is wild to have “experts” be those with 2 or 3 years of experience. Having used the platform for 11 years, now it is crazy to believe I have more experience than 99.9% of the field.
I was being confused by blocked UI threads TWO decades ago (AWT or Swing or something), so I'm confused that it was confusing you one decade ago.
You either have to split linear execution (hard although async can make this easier in languages like Python) or go multithreaded with its own difficulties.
But IME what typically happens, at least in desktop apps, is that doing a little work in the UI thread is fine until it isn’t, and the “not fine” moment can be months or years away from when the code is written. The bug reports are often vague and rare enough that it takes a while to see the pattern (eg: “it froze and then I killed the program”). And after enough of these problems, someone with experience puts a framework and some rules in place that move tasks off the UI thread. Which works until they creep back in.
When we were (slowly, painstakingly) making the Firefox UI compatible with multi-thread and multi-process, we hit this kind of issues all the time. One of the reasons we introduced Promise and async/await (née task/yield) in JavaScript was to give us a fighting chance to at least look at the code and understand where it could all break. And then we had to come up with the tools to actually debug these...
This begs the question of what is a reasonable programming model? In the MacOS case, the forcing function was buying NeXT, using their Unix kernel for MacOS, and literally firing the OS engineers who disagreed with preemptive multitasking.
For these browsers, is there a programming model that could be instituted where processing in these handlers didn't hold up the main UI thread?
Making JavaScript (conceptually) runs in the UI thread was imho one of the mistakes owed to the extremely simple early versions of JavaScript. We would be better off if JavaScript was preemptively scheduled (whether by the browser or by the OS) with language primitives to lock the UI in specific execution sections.
If JavaScript 1.0 had included such a primitive you could run all other JavaScript in the background. Alas, the JavaScript we have is essentially putting all code into such an lockUI block, and this assumption is baked in to a lot of code
And then we’d have the popular web frameworks just taking the lock and running all user code inside it, and everything would be the same as today.
Ninja edit: there haven’t been any until workers.
It's surely a nice exercise, but I really hope this is not production code anywhere. If it is then I'm not surprised about the current state of web software (myself working in a very different area, so I don't really have a clue).
It works in all browsers and it breaks long tasks. Sometimes it's even more powerful than scheduler.yield().