| > Where are you getting the idea that (one-shot) delimited continuations (stackful) "don't have the same performance characteristics" as stackless continuations, especially in a language with a JIT like JS? No matter the language, doing more work is always more costly than doing less… Because of the GC (and not the JIT) at least you can implement moving stacks in JS, but that doesn't mean it comes for free. > Also, "stackless coroutines without async/await" would give you (stackful) delimited continuations (albeit not multi-prompt). The reason Rust needs stackless coroutines is because of its commitment to "zero-cost abstractions" and high accidental complexity (and partly because it runs on top of a VM it doesn't fully control); surely JS has a different philosophy -- and it also compiles to machine code, not to a VM ??? > As to semantic differences, what is the difference between `await asyncFoo()` and `syncFoo()`? That's an easy one. Consider the following : GlobalState.bar=1;
syncFoo();
assert(GlobalState.bar==1);
The assert is always true, because nothing could have run between line 2 and 3, you know for sure that the environment is the same in line 3 as in line 2.If you do this instead: GlobalState.bar=1;
await asyncFoo();
assert(GlobalState.bar==1);
You cannot be sure that your environment in line 3 is still what it was in line 2, because a lot of other code could have run in between, mutating the world.You could say “global variables are a bad practice”, but the DOM is a global variable… |
It comes at extra work for the language implementors, but the performance is the same, because the generated code is virtually the same. Or, to be more precise, it is the same within a margin of error for rare, worst-case work that JS does anyway.
> ???
Rust compiles to LLVM, and it's very hard to do delimited continuations at no cost without controlling the backend, but JS does. Also, because Rust follows the "zero-cost abstractions" philosophy, it must surface many implementation details to the caller, like memory allocation. This is not true for JS.
> The assert is always true
No, it isn't. JS isn't Haskell and doesn't track effects, and syncFoo can change GlobalState.bar. In fact, inside some `read` method the runtime could even run an entire event loop while it waits for the IO to complete, just as `await` effectively does.
Now, you could say that today's `read` method (or whatever it's called) doesn't do that, but that's already a backward compatibility argument. In general, JS doesn't give the programmer any protection from arbitrary side effects when it calls an arbitrary method. If you're interested in paradigms that control global effects and allow them only at certain times, take a look at synchronous programming and languages like Esterel or Céu. Now that's an interesting new concurrency paradigm, but JS doesn't give you any more assurances or control with async/await than it would without them.