Because to implement it at a higher level means changing how the environment the code executes in works, which has to wait for browser vendors to rebuild how parts of their JS runtime works. It can be done naïvely quite easily – just execute all code in the program inside an
async closure which could be added by something like Babel – but this is not really a great solution.
Async await on its own is just sugar for promises, and also can be implemented entirely using generators, and generators can be implemented using [lots of] basic JS, so is completely backwards compatible once there is a syntax transform for the keywords using something like Babel. It doesn’t need the runtime rebuilt.
And async await came first: you have the await keyword which can be used in a scope where the async keyword is applied. That’s how it works. So if you then realise, actually, it would be useful to have top-level await, need to re-engineer how the whole thing works in certain situations.
Nope, JS is single threaded, it puts it into a queue, and the program continues, and when the promise is resolved the JS engine will attempt to pop it off the queue when there is a free tick.
JS can have things on different system threads by using workers, but that is a completely different thing, fairly unwieldy and they have to send everything they do back as a message to the main thread.
Node had a few threading libraries when it first appeared: it’s a C++ codebase so people just made extensions. Mostly been nixed as they break the model of JS.
WASM will allow threads at some point, but WASM code, although it’s C-level basically, is heavily sandboxed with only specific allowed interfaces between it and “normal” JS code, it’s not general purpose.
They aren’t making random decisions: the overriding principle is that everything that already exists in the language has to work the way it did before, as much as that is possible. This means that, once async await was ratified and part of the language, the way it actually works can’t be drastically adjusted. So if the behaviour of async await is changed (which it has been to an extent, although in some respects top level await is not exactly the same thing as await in async await), it cannot a. break the existing behaviour at all and b. cannot introduce performance penalties in the existing behaviour
Edit: as examples of last point, you cannot have a new version of JS in the same way as a new version of, say, Python. You can only add to what already exists. So any error in design can’t be directly rectified.
var has numerous problems, but the behaviour of
var cannot be altered, so they added two new keywords (
let). The standard
Date object is broken in numerous ways, and so an entirely new standard object (
Temporal) is currently proposed for addition. The array method
flat couldn’t be called the more sensible
flatten because that name was taken by a (jQuery competitor) library called Prototype years ago, and that library’s method has very slightly different semantics to the new standard method. And so on.