Conceptually, a `<Suspense>` component acts like a `try` boundary around a given component subtree. If _any_ component inside of that subtree suspends, it needs to "bubble up" to the ancestor `<Suspense>`.
But, React implements the core component tree rendering logic via a single `while` loop that iterates downwards. That's flat, logic-wise, whereas the tree is nested.
Meanwhile, React already had similar behavior for its error boundaries, where a thrown error in a component would get caught by the component rendering logic and it would "bubble up" to the nearest error boundary.
So, they opted to implement Suspense's mechanics the same way, except that instead of throwing an error, you throw a `Promise`.
Meanwhile, React components on the client have always been pure synchronous functions. No `async/await`, no generators, and thus no support for returning a promise from a function.
With React Server Components, React now supports `async function` components _on the server only_. They've done some prototyping with support async components on the client (and I think even briefly accidentally had a couple releases where that technically was turned on), but there's some kind of either technical issue or release planning issue that's kept them from building out that support for client components (possibly support for `AsyncContext` in browsers).