Note that does do not natively reflect structured concurrency - and a Promise.any version does even less so.
The code above would „return“ once the first Promise runs to completion. The computation that is described by the second Promise would however continue to run - which could cause unexpected side effects. The goal of structured concurrency is to totally avoid those by making sure the lifetime of child tasks is always contained within the lifetime of their parent tasks.
Bare async/await only marginally guarantees that, and the amount depends on the language. Eg the JS code above will never guarantee it, since each async function is really a separate entity on the eventloop. Rust async/await can do it if you all use are low level future combinators (like futures::join! or select!. But if the code spawns any child tasks then all bets are off too.
The Java example with scoped executor will have proper guarantees. Waiting on the executor means waiting for all tasks to complete. And as soon as the first task errors other tasks are „asked“ to stop early via an InterruptedException.
Ah thanks, I didn't think of it like that. I've never used actual structured concurrency in JavaScript or Rust, but I python I have worked on codebases that do this. Seems increasingly common.
It seems like most of the time you don't want or need the full flexibility of async/await, but I don't think the guaranteed structure is worth the benefits to me if the language doesn't support it natively. Too much boilerplate, and static analysis is usually pretty good about catching mistakes, in python at least.
The code above would „return“ once the first Promise runs to completion. The computation that is described by the second Promise would however continue to run - which could cause unexpected side effects. The goal of structured concurrency is to totally avoid those by making sure the lifetime of child tasks is always contained within the lifetime of their parent tasks.
Bare async/await only marginally guarantees that, and the amount depends on the language. Eg the JS code above will never guarantee it, since each async function is really a separate entity on the eventloop. Rust async/await can do it if you all use are low level future combinators (like futures::join! or select!. But if the code spawns any child tasks then all bets are off too.
The Java example with scoped executor will have proper guarantees. Waiting on the executor means waiting for all tasks to complete. And as soon as the first task errors other tasks are „asked“ to stop early via an InterruptedException.