Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

I'm a strong believer in structured concurrency over async.

That said, I do not know if there would be an easier way to implement the state machine in the article using structured concurrency over async. Maybe that is actually one place where async would be better.

I need to look into that.

However, for those in the comments arguing that async is better for I/O-bound stuff, I heavily disagree.

I implemented a multiplexer system. You can start any number of operations you want and then multiplex them. This blocks until one operation is done, yes, but hey, you have threads, so use another to do something else if you need.

But this multiplexer allows me to decide what function gets called for each type of task that finishes. This means that the caller still controls what to do then the "future" completes.

So it's equivalent to async, but it's still synchronous. Nice.

I can have it multiplex on multiple types of things too. I actually haven't implemented asynchronous I/O with it yet; I mostly use it to multiplex on child processes.

So I agree with one of the top-level comments: async is a hype. There are better ways for most use cases.



> So it's equivalent to async, but it's still synchronous. Nice.

Based on your description this is equivalent to async/await implemented with callbacks but not async/await implemented via polling.


Do you mean polling, as in calling poll()? Or polling, as in continuously checking if something is finished?


The latter.


In the broad use case where you have an IO-bound server, how is this solution better than async? It sounds like your non-async solution was to reimplement async, which shows the value of the feature.


I reimplemented async, except that the event loop is only invoked explicitly and under the programmer's control.

I know it doesn't sound like a lot, but it is.


Is the idea that you might be fanning out and want to delay starting the event loop? Fanning out in JS looks like:

  const fn = async (arg) => { ... };  // calls some RPC
  const results = await Promise.all(args.map(fn))
which might technically be starting the first func before the second is in the event loop, but I don't see why that matters for what I'm doing.


In Rust (which is the async/await system mentioned in the article), futures don't start until they're polled, so doing the pattern you describe wouldn't even have the ordering part you mention (unless you wanted it to, in which case you could use something like Tokio's `spawn`).


It does matter for what I'm doing.


You said most use cases, so I was thinking of a typical backend that's handling requests, calling RPCs, doing DB queries, etc.


async event loops in Rust are invoked explicitly by the programmer as well.


Are they always invoked explicitly? Or is it sometimes implicit?


The closest to implicit you can get is the `#[tokio::main]` attribute macro [1], which expands to something like this

    fn main() {
        tokio::runtime::Builder::new_multi_thread()
            .enable_all()
            .build()
            .unwrap()
            .block_on(async {
                println!("Hello world");
            })
    }
[1]: https://docs.rs/tokio/latest/tokio/attr.main.html#using-the-...


That's technically something the runtime implementation can choose, but for Tokio, the most common runtime, the sibling comment is correct that the only way to avoid having to explicitly define it is to use an annotation on the main function (which just generates the call to instantiate the runtime via a macro).


That's technically something the runtime implementation can choose, but for the most common runtime, Tokio,




Consider applying for YC's Winter 2026 batch! Applications are open till Nov 10

Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: