Reference: Asynchronous Closures/Futures

With all the different types of asynchronous closures and futures having been introduced, it may become difficult to keep track of where and when they execute in different contexts. The following table briefly describes when and where the code is executed for each of the introduced asynchronous closure types. Note how each asynchronous closure type behaves differently when executed on the server and on the client in conjunction with the relevant stage of execution (SSR/hydration/CSR).

Server-side renderingDuring hydration (preparing for CSR)Client-side rendering
Server Functions (Server)Invoked as a normal asynchronous function, with the code defined in the body directly executed and the results returned to the caller. When executed inside a component, the full reactive context is accessible.-(Invoked by responding to the HTTP request made to the server function's standalone endpoint, where the response will be the serialized result. Since this endpoint is fully standalone, reactive context is typically not available.)
Server Functions (Client)-Not executed. (When defined inside resource typed async closures.)Makes an HTTP request to the server function's endpoint, the response body will be deserialized into the result. Take note that the code in the function body is never compiled into the client.
Resources (Server)Their async closures are executed in parallel when possible by the async executor; results are encoded into the response body inside <script> tags.--
Resources (Client)-Not executed; the <script> tags within the response body provides the results.Their async closures are executed whenever required by the reactive system to return any new results.
Local Resources (Client Only)-Not executed. (Will be executed once after hydration to provide the initial value.)Their async closures are executed whenever required by the reactive system to return any new results.
Suspend (Server)These futures are executed in parallel when possible by the async executor. The selected SsrMode can modify how and when: 1) these futures are polled, 2) the ordering of the stream of outputs.--
Suspend (Client)-These futures are fully executed once more in the client as part of hydration; returned view! {} will be used to hydrate the HTML.These futures are executed whenever required by the reactive system; returned view! {} will be rendered wherever they are defined.

A useful note to keep in mind is that there are up to three different stages of execution, split between both the server and the client. One consequence of this are the potential for hydration bugs or unexpected program behavior if there are side effects inside a Resource or Suspend. These side effects may not be executed in the expected time or in the identical order across the different stages of execution, and this mismatch may lead to hydration errors.

For example, a side effect might arise from a resource that sets a signal. The resource might pass a value that's sourced asynchronously through a reactive signal in its asychronous closure - this behaves perfectly fine under SSR and CSR, but will not behave in the expected manner if hydration is involved. During hydration, the body of the asynchronous closure will not be executed, thus resulting in the signal not being set during hydration; and since that closure under SSR would be executed and thus the signal would be set there, this mismatch between the signal being set under SSR and not set during hydration may lead to a hydration error. To correct for this, have the resource return the value, so that a Suspend may .await for it before passing it through the reactive signal, ensuring the signal is set under all three execution stages.

Another way this mismatch may arise is when a single signal is used by multiple Suspends. The Suspends may be polled in a different order on the server versus how they may be polled on the client; this is simply due to the different async executors being used in these two contexts. This effectively results in an unstable order of execution, leading to the changing of the order of which the values are passed through the signal. Ultimately the result is yet another potential for values being mismatched under SSR versus hydration, and in this particular instance the hydration error may become non-deterministic and thus difficult to narrow down.

As another example, during SSR, server functions will have access to the full set of contexts set up by the application, as they will be invoked as standard async functions during rendering of the application. On the otherhand, their API endpoint counterparts exist as much more simple standalone endpoint handlers, thus they will run without any of the contexts that the application have set up. Hence the use of expect_context and use_context on types that are set up by the application will not work, unless the values were provided to context by .leptos_routes_with_context().

The above are just some examples to serve as a quick reference of the behavior of the different types under the typical documented configuration (i.e. server functions are only invoked/awaited inside Resources, and Resources are only awaited inside Suspends), as combining or nesting of these asynchronous closures in some other manner can cause other behaviors.