5 ms·
That's 'lying' though, async/await is a great way to know which of your functions perform i/o and which do not. Namely in platforms like Node the convention is
by inglor 7y ago
That's 'lying' though, async/await is a great way to know which of your functions perform i/o and which do not.
Namely in platforms like Node the convention is to not do synchronous I/O and as a communication tool - everything that doesn't use async/await is promised to never block or perform I/O.
You might claim it's a lower level of abstraction but it makes concurrency a _lot_ simpler and it's why often when I write go I have to write 35 lines for something that would take me 5 in C#, Python, JavaScript or now Rust.
- divan 7y agoThanks, that's interesting point. I see how it might be useful, but it's still looks as a minor optimization that doesn't outweigh the drawbacks of the model. How do you deal with a situations when you have non-async/await code, and then you need to call just one function, which happened to be "async", and you can't do until you mark all your functions async as well? I find myself in this situation too often with Dart/Flutter, and it's especially embarrassing experience as it happens usually with a simple parts of code, where I don't need concurrency at all (like, get local directory name and store file to it). This "forced refactoring" also hits language design corners like "you can't call async functions" in constructor or `iniState()` and I end up writing async wrappers and other helpers functions just to make the whole thing work. > it makes concurrency a _lot_ simpler I find people put different meaning in the word "simpler". Do you mean that code requires less lines/chars to write or that it's easier to read and understand (those are totally different properties, often conflicting)?
- asark 7y agoI hate Node's model of async concurrency. I need one thing to happen, then another depending on the result of the first, and so on, way more often than I can let several things happen at once or in any order. Probably something like 100x as often. Even when I can have multiple pieces of logic executed at once or in any order, it's usually just a couple, and internally they usually comprise more of the same dependently-ordered execution. If I can really kick off a ton of something at once it's usually one function/operation, which again... consists, internally, of a bunch more stuff that needs to happen in a certain order. It'd be way more convenient if Node just assumed it couldn't carry on with the next operation until the current one was done, even if the current operation is async I/O—feel free to go off and process something else, just no more of my code—unless instructed otherwise. IOW, caller decides, yes. I 100% do not understand the appeal of a system where you have to specify that you do want the next instruction to wait on the result of the previous one. "But you'll block node's single thread during your I/O!" no, it can go do something else, just no more of this particular line of logic in my code unless I specifically tell it otherwise, thanks. Caller decides is both easier to follow and more useful.
- laughinghan 7y agoSounds like you might prefer Erlang or Go. But to be clear, "it'll block Node's single thread!" is not the reason for explicit await. The reason for explicit await is to guarantee that after calling a function foo(), only the state that foo() changes could have changed. Whereas after awaiting on an async function bar(), or any other promise, literally any state-mutating code in the entire application might have run. Obviously plenty of languages have this problem and deal with it, usually with locks like in Go and Java, or by banning shared mutable state entirely and only allowing message-passing for communication between concurrent "lines of logic" like in Erlang. (As far as I understand Go basically tries to get the best of both worlds by strongly encouraging message-passing, with locks intended only for advanced situations like custom concurrent data structures.) But there's no way for Node to change to either of those models without breaking every single nontrivial piece of code ever written in it. And apparently Rust intentionally chose not to go with those models because they require a runtime and make compatibility with C very difficult, well-known advantages over Go.
- staticassertion 7y agoI think this article justifies 'simpler' as more than just "characters to write". Rust is a fairly different language in that it isn't GC'd, and the async/await sugar really alleviates a lot of pain around that (among other things). As for calling async from sync, I imagine you just create a runtime and block on it.
- ClumsyPilot 7y agoIn C# you can't await an async in non-async function, but you can block until it's done. There is no drawback to having all code async.
- TravHatesMe 7y agoThere is a drawback: it pollutes the signatures of each asynchronous method and the signatures of the consumers. Each method now returns a Task, and each method name ought to have a suffix of 'Async'. It sucks that changing this will require all consumers to also modify their signatures as well. But I understand why it is necessary, the compiler needs to mark each 'step' in the asynchronous function to create a kind of state machine.
- Rusky 7y agoBut it doesn't pollute the signatures of the consumers, that's the whole point of this subthread. If you don't want/need to expose your async-ness, you just synchronously block on your async callees instead of awaiting them.
- bryanlarsen 7y ago> How do you deal with a situations when you have non-async/await code, and then you need to call just one function, which happened to be "async", and you can't do until you mark all your functions async as well? Just call the poll function on your Future. Just because Javascript sucks doesn't mean the similar feature in Rust does too. :)
- Tuna-Fish 7y ago> How do you deal with a situations when you have non-async/await code, and then you need to call just one function, which happened to be "async" This depends on what do you want to happen. Do you want to block on the one async function, or do you want to turn your current function into async and propagate the asyncness to your caller? Both options are available to you in Rust.
- ummonk 7y agoI think the people criticizing it are, like me, coming from other languages where blocking on async doesn't make your function sync. The asyncness still ends up propagating to the caller function in such languages.
- Tuna-Fish 7y agoThank you for putting it explicitly, everything here suddenly makes so much more sense. But yes, in Rust async is not a plague that irredeemably taints everything. You can choose to use anything that is async synchronously if you choose to, just by calling .poll() on the future at the callsite.
- michaelmior 7y ago> everything that doesn't use async/await is promised to never block or perform I/O I don't think that's true. See fs.readFileSync for example. You could say that's an exceptional case (which I think it is), but it means that there's no "promise" that functions without async/await don't perform I/O.
- inglor 7y agoRight, there is always an escape hatch, this is also true for haskell with unsafePerformIO - because of practicality. That's a good thing :]
- heavenlyblue 7y agoa) this function is clearly marked as sync b) the question of why does it exist if the async version if available is beyond me
- eropple 7y agorom a practical perspective, it's much easier to deal with `fs.readFileSync` in top-level code than to dive face-first into callbacks. (Remember, `fs.readFile` doesn't return a `Promise`.) If `fs` was promise-aware and top-level await existed, it would be a different conversation. As-is, for writing a one-off script, avoiding callback hell is pretty nice.
- michaelmior 7y agoAgreed. I've used it from time to time for writing simple scripts and it does make things much easier. You don't really have to worry about a CLI script blocking.
- amaranth 7y agoTop-level await is still a problem but a Promise version of the `fs` API has been available since Node v10. https://nodejs.org/api/fs.html#fs_fspromises_readfile_path_options https://nodejs.org/api/fs.html#fs_fspromises_readfile_path_o...