参考:异步闭包/Future
Reference: Asynchronous Closures/Futures
随着各种不同类型的异步闭包和 Future 的引入,跟踪它们在不同上下文中执行的时间和地点可能会变得困难。下表简要描述了引入的每种异步闭包类型在何时何地执行。请注意每种异步闭包类型在服务器和客户端上,以及结合相关的执行阶段(SSR/注水/CSR)时的行为差异。
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).
| 服务器端渲染 (SSR) | 注水 (Hydration) 期间(为 CSR 做准备) | 客户端渲染 (CSR) | |
|---|---|---|---|
| Server-side rendering | During hydration (preparing for CSR) | Client-side rendering | |
| 服务器函数 (Server 函数体) | 作为普通的异步函数调用,直接执行函数体中定义的代码并将结果返回给调用者。在组件内部执行时,可以访问完整的响应式上下文。 | - | (通过响应对服务器函数独立端点的 HTTP 请求来调用,响应将是序列化的结果。由于此端点是完全独立的,响应式上下文通常不可用。) |
| 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.) |
| 服务器函数 (Client 调用侧) | - | 不执行。(当定义在 Resource 类型的异步闭包内时。) | 向服务器函数的端点发起 HTTP 请求,响应体将被反序列化为结果。请注意,函数体中的代码从未被编译进客户端。 |
| 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) | 它们的异步闭包由异步执行器尽可能地 并行 执行;结果被编码到响应体内的 <script> 标签中。 | - | - |
| 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) | - | 不执行;响应体内的 <script> 标签提供结果。 | 每当响应式系统需要返回任何新结果时,就会执行它们的异步闭包。 |
| 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) | - | 不执行。(注水后将执行一次以提供初始值。) | 每当响应式系统需要返回任何新结果时,就会执行它们的异步闭包。 |
| 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) | 这些 Future 由异步执行器尽可能地 并行 执行。选定的 SsrMode 可以修改:1) 这些 Future 何时被轮询,2) 输出流的顺序。 | - | - |
| 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) | - | 这些 Future 作为注水的一部分在客户端完整执行一次;返回的 view! {} 将用于注水 HTML。 | 每当响应式系统需要时,就会执行这些 Future;返回的 view! {} 将在它们定义的地方渲染。 |
| 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. |
需要记住的一点是,执行阶段多达三个,分布在服务器和客户端之间。其中一个后果是,如果 Resource 或 Suspend 内部存在副作用,可能会出现注水错误或预料之外的程序行为。这些副作用在不同的执行阶段可能不会在预期的时间或以相同的顺序执行,这种不匹配可能导致注水错误。
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.
例如,副作用可能源于设置信号的资源。资源可能会在其异步闭包中通过响应式信号传递一个异步获取的值——这在 SSR 和 CSR 下表现完美,但如果涉及注水,则表现不符合预期。在注水期间,异步闭包的主体 不会 被执行,从而导致信号在注水期间未被设置;而由于该闭包在 SSR 下会被执行并设置信号,SSR 下信号被设置与注水期间未设置之间的这种不匹配可能导致注水错误。为了纠正这一点,让资源返回该值,以便 Suspend 在通过响应式信号传递该值之前可以 .await 它,从而确保在所有三个执行阶段都设置了信号。
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.
产生这种不匹配的另一种方式是当多个 Suspend 使用同一个信号时。Suspend 在服务器上的轮询顺序可能与客户端上的轮询顺序不同;这仅仅是由于这两个上下文使用了不同的异步执行器。这实际上导致了不稳定的执行顺序,从而改变了值传递给信号的顺序。最终结果是又一个 SSR 与注水之间值不匹配的潜在可能,而在这种特定情况下,注水错误可能变得非确定性,从而难以缩小范围。
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.
再举一个例子,在 SSR 期间,服务器函数将可以访问由应用程序设置的全套上下文,因为它们在应用程序渲染期间将作为标准异步函数被调用。另一方面,它们的 API 端点对应物作为更简单的独立端点处理器存在,因此它们运行时没有任何应用程序设置的上下文。因此,对应用程序设置的类型使用 expect_context 和 use_context 将不起作用,除非这些值是通过 .leptos_routes_with_context() 提供给上下文的。
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().
以上仅是一些示例,用作在典型文档配置下(即服务器函数仅在 Resource 内部被调用/等待,Resource 仅在 Suspend 内部被等待)不同类型行为的快速参考,因为以其他方式组合或嵌套这些异步闭包可能会导致其他行为。
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.