1. 32
  1. 10

    Compare that to JS, where every .then() has to create a new Promise object and dynamically add it to an internal list of listeners of the parent Promise. The chains (DAGs, actually!) are built at run time.

    Rust takes advantage of its single-ownership rules to ensure that each Future can have only one owner. future.then() consumes the future, and the newly returned object is the only instance of the entire chain.

    In JS cancellation of promises is a messy problem, because there’s no clear ownership of the graph of promises (various chains could have been joined together, so it’s not even clear where it starts and what to cancel). OTOH in Rust there’s always only one copy of each chain, with a single owner, so when it’s dropped, the operation is cancelled.

    1. 2

      I don’t have much experience with JS but Python’s async preserves stack traces. Does Rust’s too?

      1. 4

        Yes, kind-of. These aren’t quite comparable, because Rust doesn’t use exceptions.

        Futures are polled, so if a future panics (stop-the-world fatal error, equivalent to a program crash) during polling, there will be a useful stack trace showing what polled it.

        OTOH normal error handling is done by returning errors, and errors are just plain values (if a function returns you false, the boolean doesn’t have a stack trace where it came from — unless you explicitly wrap it in a type that adds such info, and some Rust libraries do that).

      2. 1

        How do chains get joined? I’m probably misunderstanding, but wouldn’t that imply that two then invocations in two different promise chains could be coalesced into a single Promise object?

        1. 2

          There are join and join_all that join 2+ futures into one. The key aspect is that the joined futures cease to exist elsewhere, and they become integrated in the state machine.

          There’s also a wrapper that makes a future shareable, but that’s special mechanism that needs to be invoked intentionally (similar to Arc<Mutex> wrapper for non-async normal types).

          1. 1

            Oh, thanks! I’m familiar with that mechanic in Rust, but I was actually asking about the JavaScript implementation.

            1. 1

              JS objects can be individually monkey-patched, so reuse of Promise objects would be visible. I don’t think the spec allows that. Until recently even async function() {return new Promise()} would create another promise instead of returning the one it got.