When I’ve used futures or promises in the past, it was very common for me to call 2-3 async functionsin a row and then await all futures at once. That way the calls would run in parallel in the background.
In fact, I think it would be really nice if I could write
let a = fetch("Alice");
let b = fetch("Bob");
let users = await (a, b);
If we had IntoFuture that could make sense to implement for a tuple, it’s impossible to directly implement Future for a tuple as it’s lacking somewhere to store the resolved values while the rest of the futures complete.
Awesome! Thanks for the link. I’m very happy to see that this is implemented as a “user-space” macro.
A constant theme throughout the await syntax discussions has been the need for it to be chainable. However, it’s my experience that combinator macros like join!(...), try_join!, and select! are much more important.
Something else I need to mention is that with Futures 0.3, Futures are no longer required to have an error type. That means that Futures which error need to return Result (just like regular functions).
(That’s actually a consequence of a tag being pushed to the wrong branch, those are the 0.3 docs, the URL just includes 0.1.27. I was going to delete that folder but since it’s been linked to I thought it better to leave it online. I’ve removed it from the index now so that hopefully it won’t be linked to again).
For some reason I expected this to be like select(), but that doesn’t seem like the case? Will there be support for a version of join that awaits all the futures simultaneously, and returns the result of the version that returns first? (Typing this seems like a problem, since you want to turn for<Ts..> (Future<Output=Ts>...) into for<Ts...> enum(Ts...)…)
The net result of this is that the error handling becomes asynchronous. The way the async/await discussion is presented in the final proposal (in particular the “error handling problem”) seems to indicate that error handling for Rust futures should be synchronous. That is, you get a Future<Result>, you await it, you look at the error, and then you continue with your work.
Yes, that is correct. It’s not a new development, it was done many many months ago.
The end result is that we can now correctly use Futures which don’t error, and we gain significantly increased consistency and orthogonality.
It means that async functions aren’t much different from normal functions, other than their ability to yield. A normal function returns a Result and handles errors with ?, and an async function does the same thing.
This all happens within a single tick, on the microtask queue (which is why it’s synchronous, not asynchronous).
Let’s compare that to Rust. It waits for the Future to resolve, then synchronously checks whether it’s an error or not (using pattern matching).
Essentially, Rust is just cutting out the final “call the appropriate callback” step, everything prior to that is the same.
I don’t really want to get into a syntax argument, since I think that’s pretty off-topic in this thread, but what you’re saying is that instead of this…
let (a, b) = try_join(
…we should instead have this:
let (a, b) = await(try_join(
Personally, I consider the first one visually much more appealing. And it is objectively easier to type and understand (since it has fewer parentheses and it doesn’t require you to jump back and forth).
Things like join and try_joincan be functions, but awaitcannot be a function. So making await stand out and look different can be an advantage, not a disadvantage.
Keep in mind that the join / try_join / etc. functions are usually combined with.await (as you can see in the above example), so it’s better to think of foo().await as a single visual unit, similar to how .await? is a single visual unit.
The Rust team does a really good job, and I have a lot of respect for them. I think the only thing that could be done better is to have some sort of system for summarizing information, since right now the discussions are spread out all over the place, making it hard to find.