6 ms·
Thing of it as a different analogy. There are various chores to do around the house - some of them must be done synchronously (do the ironing - we don't want t
by PJDK 7y ago
Thing of it as a different analogy. There are various chores to do around the house - some of them must be done synchronously (do the ironing - we don't want to leave the iron on). Some of them can be done asynchronously (run the washing machine). If you want to write out your tasks it might look something like this.
LoadWasher() (sync)
washerRunning = RunWasher() (async)
var cleaning = CleanKitchen() (async)
wetClothes = await washerRunning;
LoadDryer(wetClothes)
clothesToIron = await RunDryer() // while we're awaiting we walk back up the stack and can continue cleaning the kitchen until the dryer is finished
IronClothes(clothesToIron) // this is just happening synchronously
await cleaning //last job to do so we need to make sure we finish!
Don't know if that helps, but I think it's an interesting analogy
- spinningslate 7y agoThat is a good example. It illustrates that async is a partial abstraction on the way to dataflow. It's not immediately obvious from your example what's really going on: whereas re-writing it as dataflow pipelines makes the intent clear: LoadWasher() | RunWasher() | LoadDryer() | RunDryer() | IronClothes() CleanKitchen() where `|` is piping the output of one operation into the next one (as per *nix shell). The two lines are independent and so can be scheduled according to resources available. Doing so makes the inherent dependencies trivially clear. It doesn't matter which are "long" running and which aren't: it's not possible to run the washer until it's been loaded. Using async/await conflates two things: 1. What's "long" running, for some definition of "long" (--> make it async) 2. What's dependent on what (await) -- EDIT: fix formatting & added missing "RunDryer()" step
- dmix 7y agoSo you basically you'd have to rely on each of the function signatures, probably with some text editor/IDE by hovering over them, to figure out which one in the pipe list is Async? Or jump to each of them with ctags. Then wrap it in a try/catch or pattern match the result to handle the various errors different errors...
- spinningslate 7y ago> So you basically you'd have to rely on each of the function signatures, probably with some text editor/IDE by hovering over them, to figure out which one in the pipe list is Async? Or jump to each of them with ctags. No, the point is you don't _need_ async. At least not as a language primitive. Instead, one expresses directly where the data dependencies lie. If an operation in a pipeline blocks, the runtime is at liberty to switch to another task. That already happens today, whether in language runtimes (e.g. Node) or the underlying OS. I don't need to tell the runtime "this operation might block". It already knows. Instead, we've explicitly defined intent: when `RunWasher()` finishes, `LoadDryer()`. It doesn't matter if the washer takes one micro second or one hour; I can't load the dryer until it's done. >Then wrap it in a try/catch or pattern match the result to handle the various errors different errors... True, error handling is missing. But then it's not in the parent example either. The requirements aren't fundamentally different in either case.
- amelius 7y agoThe problem is that you don't always know beforehand whether you'd like to perform a task synchronously or not.
- Sharlin 7y agoIt’s easy to run an async function synchronously; just drop the `await` keyword and wait on the returned future.
- amelius 7y agoAnd so all library functions should be async by default? And if everything is async, then shouldn't synchronous programming require the extra keyword instead of the other way around?
- Sharlin 7y agoNo. A function that makes no async calls has no reason to be marked async.
- amelius 7y agoAnd if someone wants to add an async call later, all callers need to change too.
- Sharlin 7y agoOnly if the function "leaks" the async-ness instead of containing it (that is, waiting on any async calls synchronously). But yes, making a function async is a backward-incompatible change, just like manually changing the return type from `T` to `Future<T>`.
- amelius 7y agoOk let me phrase it differently. If you have some library function that does some IO (for example) and you want to make it as useful as possible, would you declare it async or not? If you make it blocking, then you'd ruin any opportunity to have it called from async functions (because they don't want to be blocked). Therefore, you'd make it async. So that means that all IO library routines will become async, and possibly a lot of others too. Wouldn't it make more sense to have async be the default?
- Zxian 7y agoYour analogy works as long as there's a machine doing the washing and drying. If you replace the machines with people, it makes much more sense for the function definition to define what is and isn't synchronous. If you have to do all the chores yourself, just call the functions synchronously. If you have family members you can delegate some work to, you can call the functions asynchronously and wait for them to complete while you're doing your own work (or twiddling your thumbs). Elixir allows you to do this very cleanly. https://hexdocs.pm/elixir/Task.html#content https://hexdocs.pm/elixir/Task.html#content
- dleslie 7y agoWouldn't it be desirable to replace async with sync, then? I'd rather dirty the signatures of blocking/synchronous methods then of the rest.
- Cyph0n 7y agoUnlike JS, most languages, including Rust, are not async by default, so this would not make sense.
- dtech 7y agoHow is JS async by default?
- steveklabnik 7y agoWhen people say this, they usually mean "a significant amount of things JavaScript is used for are asynchronous, and IO is virtually always asynchronous," rather than something more literal. The language has asynchronicity deep in its bones, even if it's not entirely made out of it.
- PJDK 7y agoThat would make a lot of sense, but wouldn't be very practical to add in to an existing code base.
- dtech 7y agoSync is still much more common than async. I'd rather write `await { httpCall }` than `blockOn { 1 + 1 }`
- z3t4 7y agoAnother problem with await is that you can't run in parallel without going down one abstraction layer. So it just add additional complexity as you need to know two layers. The hardest part with software is to not implement too many features.
- CJefferson 7y agoMy problem with this is if 'I' (or in code, the current program) actually loads the washer and cleans the kitchen, then these things run in serial. If I do what I've always done in rust, which in use threads, I get lovely easy parallelization. Async only seems useful when your program doesn't do anything non trivial, but just dispatches to other things, as any one function that takes a long time blocks all other async functions until completion?
- shepmaster 7y agoYes, but you can run your blocking code on a thread which presents itself as a future: - What is the best approach to encapsulate blocking I/O in future-rs? — https://stackoverflow.com/q/41932137/155423 https://stackoverflow.com/q/41932137/155423