6 ms·
Internals of async / await in JavaScript
- rzimmerman 3y agoIt's obsolete now, but before async/await was part of JS proper I worked on a compile-to-JS language that handled this by compiling to callbacks: https://github.com/rzimmerman/kal https://github.com/rzimmerman/kal It included try/catch support and fancy stuff, like loops. The source may be interesting for anyone interested in compilers: https://github.com/rzimmerman/kal/blob/master/source/generator.litkal https://github.com/rzimmerman/kal/blob/master/source/generat... The Kal compiler is written in Kal, but it's supposed to be easy to read. Surprisingly the browser demo still works: http://rzimmerman.github.io/kal/demo.html http://rzimmerman.github.io/kal/demo.html
- sebazzz 3y agoBefore it was par of JS proper Typescript could also transpile to callbacks. Good stuff.
- 14u2c 3y agoBabel will still pollyfill async await too if configured.
- solarkraft 3y agoKal looks super cool!
- lf-non 3y agoThis looks pretty nice. Would you be interested in reviving this to target type-safe ts ? I have been looking forward to a type-safe coffee-script. Civet.dev comes close but goes into some pretty esoteric directions.
- rzimmerman 3y agoThat's a cool idea and it could be a lot more useful with modern approaches, like true type systems and pattern matching. I don't really have time to work on that but I support the effort of bringing some of CoffeeScript back to the world.
- trashburger 3y agoFrom my (admittedly shallow) understanding, each "await" in an async function creates continuation points, is that correct? And then when the internally used Promise resolves or rejects, the function resumes from the continuation point.
- dgb23 3y agoConceptually yes. Practically speaking it’s easier to grasp. The article in question makes a good point of playing around with the concepts to dispel the magic. Asnyc/await is just syntactic sugar for promises. Promises are just wrappers around callbacks. Callbacks are used to hook functions into an event loop. The dispatching/scheduling is handled there. So really when you use async/await, you’re just writing pseudo-sequential code that gets turned into callback code. This is also why a function can only await if it’s marked as async. It’s callbacks, function coloring leaking out so to speak.
- solarkraft 3y ago> Callbacks are used to hook functions into an event loop. The dispatching/scheduling is handled there. This part never really made it into my understanding of the concept. For practical purposes I've been completely fine with not thinking about it at all. My function is called at some point, that's all what I find really matters. Thinking about the event loop opens up a can of worms I find more confusing than useful.
- capableweb 3y ago> Thinking about the event loop opens up a can of worms I find more confusing than useful. Probably for daily usage this might be true, but there are definitely cases when you're using JavaScript and not understanding the event loop makes it hard to understand why things are happening. Example: setTimeout(() => console.log('hello'), 0) console.log('world') You'd normally expect this to print "hello" and then "world" since the "sleep time" for the setTimeout is set to 0, but thanks to the event loop, the result will be "world" and "hello". Very simple example, but in real life code bases and beginner JavaScript developers, in can be confusing at times.
- winrid 3y agoThought this was going to explore V8 source code...
- endorphine 3y agoThat would be cool
- quectophoton 3y agoThe only reason I know about JS generators, and also about using them to simulate async/await, is only because I was already using JS when they were added to the language. If that hasn't been the case, I probably wouldn't even know they existed. I haven't used that much JS recently, but my guess is that "for await of" will make generators more widely known and used.
- endorphine 3y agoRelated Ask HN discussion with lots of insights on how async/await works: https://news.ycombinator.com/item?id=30502067 https://news.ycombinator.com/item?id=30502067
- l5870uoo9y 3y agoThe whole article properly the best explanation of generators I have come across. This quote stuck out: > Generators are a special type of function that can return multiple pieces of data during its execution. Traditional functions can return multiple data by using structures like Arrays and Objects, but Generators return data whenever the caller asks for it, and they pause execution until they are asked to continue to generate and return more data. Applications of generators? I have only used Redux-Saga[1]. Can't even think of other libraries that use them, but would be interested in learning. [1]: https://redux-saga.js.org/ https://redux-saga.js.org/
- littlestymaar 3y agoI think seeing generators as special functions makes them feel more magic than what they really are: generators are nothing more than a syntactic sugar for objects with a “call” method that will change the values of the private fields in this object, that's why you can call it repeatedly with different results. Some patterns become easier to write with the generator syntax, but it's not really adding expressive power. (Unlike futures for instance)
- heavenlyblue 3y agoYou could also have an object with on_success, on_failure attributes so Futures are the same syntax sugar
- littlestymaar 3y agoIn JavaScript, where you have first class functions and an event loop built-in, yes. But for languages that lack one of those, then no.
- btown 3y agoIMO this is like saying a high-level language is syntactic sugar for assembly. Generators allow you to write a state machine as a procedural function whose local variables are automatically suspended when you yield and restored when the caller returns control to the generator. They compile this down to, as you said, an object with a call method. But that transformation is as nontrivial as any compilation/transpilation project.
- flohofwoe 3y agoThis doesn't go deep enough IMHO, I only really 'grokked' async/await (and how it differs from stack-switching coroutines) once I understood that it's just syntax sugar for a "switch-case state machine" (meaning you can emulate async/await-style in any language, even plain old C running on a heavily restricted VM like WASM). For instance look at the JS output of async/await Typescript when a really old JS version is used that didn't support async/await yet. It's switch-case all the way down: https://www.typescriptlang.org/play?target=1#code/FAMwrgdgxgLglgewgAgO4EM4wBQEpkDewyyATgKYxikoTmrIAKpCAtnAM7nbYUcIAbAG7l8AXgB8hYiWRcYAFTityCMDjzJJ02bL6CR2AORKVamGXL9h5ACZHcAbhkkAvgBpkAJgAMfpzKuAa7AwOgcAJ7QyODQ8EjIrJgQmkQkUEjW5AB0AggA5sYAohAwpBEOzjIZEBwWfGACFmLI6BhYaJg4AemZgjl5hQ1NAdV9AgMFxQAeWJUyFFQ0yEYASpTUEEbOIUA https://www.typescriptlang.org/play?target=1#code/FAMwrgdgxg...
- capableweb 3y agoThere is more happening than switch-cases though, evident even from your link (it mentions Promises but doesn't provide the implementation for it). I think what another comment mentioned is more accurate: > Asnyc/await is just syntactic sugar for promises. > Promises are just wrappers around callbacks. > Callbacks are used to hook functions into an event loop. The dispatching/scheduling is handled there. > So really when you use async/await, you’re just writing pseudo-sequential code that gets turned into callback code. https://news.ycombinator.com/item?id=37360151 https://news.ycombinator.com/item?id=37360151
- bheadmaster 3y ago>> So really when you use async/await, you’re just writing pseudo-sequential code that gets turned into callback code. Small nitpick: I wouldn't call it "pseudosequential", personally. By that logic, C is pseudosequential, because when you call a system call, your process state gets saved in the scheduler, control gets passed to the kernel, then when kernel finishes, your process state is resumed. Not even getting started on branch prediction and similar CPU optimizations. What I mean is, as long as you have a kernel and scheduling, there's no such thing as a real (userspace) sequential program. We just call them sequential because their side effects correspond to a sequential model of execution. So if the side effects of an async/await program corresponds to that of a sequential model, it's a sequential program. But I get the value of using that word for an explanation of the implementation.
- h1fra 3y agoGood article, love the codesanbdox! I often disregard yield because it's basically the same as having an `await for/of` or `while await` and it's not that clear for beginners.
- plopz 3y agoThe most surprising thing that's bitten me about async/await is that the promise is immediately executed rather than being executed at the point they get awaited.
- jezzamon 3y agoDefinitely something to know, but it's the best way for that to happen when you're waiting on asynchronous requests, as it lets you create the subsequent promises while the first promises is fetching its results
- plopz 3y agoIt hurts a lot though when you have a bunch of promises and you want to rate limit the execution
- vlovich123 3y agoWrap them in a generator.
- threatofrain 3y agoWhat's the status of cancellable promises in proposal land?
- capableweb 3y agoI mean, why would you want to "cancel" a Promise? You can already cancel HTTP requests, what are some other things you want to cancel really? If you don't want to continue with something inside a Promise, just pass in something that could be changed from `true` to `false` and it cancels itself based on that.
- coke12 3y agoI've had this same cancellation conversation many times with JS programmers. I think it's fair to say there's a need for standardization. Evidence for this need is network request management is really poor in pretty much all JS apps.
- _akash_h 3y agoAuthor of this article here. Thank you to everyone who read my article! Absolutely love some of the discussions going on here. My goal with the article was to peel away one layer of this abstraction and encourage curiosity in engineers who usually don't think about lower level stuff.