I'm trying to understand best practices around async vs future<T>, and when to choose each one.
Rust and JS - langs where I'm most comfortable - don't make a big difference between marking a function async vs returning a future/promise. Both have almost the same type (with only minor differences that don't affect most users).
See Rust and TypeScript examples of implementing the same functions either as async or as returning a future/promise
In wit, there seems to be a larger difference. E.g. wasmtime lets async functions be backed by a simple async rust function whereas future needs to be backed by FutureReader.
Since the semantics seem meaningfully different, what are the reasons to choose one over the other when designing a wit interface?
My recommendation would be to use async func until you have a reason not to
What would make a good reason not to use async?
If the function takes a borrow handle to a resource, the borrow won't be released until the function returns. If it's an async function, that means you have to .await it before the borrow is released. If it's a sync function, you'll get the borrow back sooner and can defer awaiting the future as you please.
Also, you might want to return e.g. tuple<something, future<something-else>>, which gives you more a bit more flexibility than an async function. Or you might even want an async function that returns a tuple with a future. You've got plenty of options :)
Thanks both!
If I can ask a follow up: what is the difference in semantics between async and future? Why is the wasmtime api so different with future?
You can think of a future as a oneshot channel which includes a writable end and a readable end. The future type itself represents the readable end, and the only things you can do with it are read (i.e. await) it, drop it, or give it away (e.g. to the host or another component instance). Whereas the subtask produced by a call to an async function can be awaited or dropped, but it can't be given away.
Another way of thinking about it is that future is for "unstructured" concurrency where you're wiring control flow up in a way that's not necessarily shaped like a call tree. Whereas recursive async function calls always end up forming a tree structure.
All I know is wasmtime kept throwing when I tried to use WASI P3 with andreiltd's componentize-qjs
Yeah, that looks like
wasmtimeissue, it seems that it doesn't flush stdout. I'm not sure if there is a guest side workaround but let me have a look.
https://github.com/andreiltd/componentize-qjs/issues/2#issuecomment-4689553760
ah, the classic doesn't flush issue.....
Last updated: Jul 29 2026 at 05:03 UTC