网络并发管理
On this page

网络并发管理

¥Network Concurrency Management

在构建 Web 应用时,管理网络请求可能是一项艰巨的任务。确保数据最新和处理同时请求的挑战通常会导致应用中出现复杂的逻辑来处理中断和竞争条件。Remix 通过自动化网络管理、镜像和扩展 Web 浏览器的直观行为简化了此过程。

¥When building web applications, managing network requests can be a daunting task. The challenges of ensuring up-to-date data and handling simultaneous requests often lead to complex logic in the application to deal with interruptions and race conditions. Remix simplifies this process by automating network management, mirroring, and expanding the intuitive behavior of web browsers.

为了帮助理解 Remix 的工作原理,请记住,在 全栈数据流 提交 form 请求后,Remix 将从加载器中获取新数据。这被称为重新验证。

¥To help understand how Remix works, remember from Fullstack Data Flow that after form submissions, Remix will fetch fresh data from the loaders. This is called revalidation.

与浏览器行为自然对齐

¥Natural Alignment with Browser Behavior

Remix 对网络并发的处理很大程度上受到了 Web 浏览器处理文档时的默认行为的启发:

¥Remix's handling of network concurrency is heavily inspired by the default behavior of web browsers when processing documents:

  • 浏览器链接导航:当你在浏览器中点击一个链接,然后在页面转换完成之前点击另一个链接时,浏览器会优先使用最近的 action。它会取消初始请求,仅关注最新点击的链接。

    ¥Browser Link Navigation: When you click on a link in a browser and then click on another before the page transition completes, the browser prioritizes the most recent action. It cancels the initial request, focusing solely on the latest link clicked.

    • Remix 的方法:Remix 以相同的方式管理客户端导航。在 Remix 应用中点击链接时,它会针对与目标 URL 绑定的每个加载器发起获取请求。如果另一个导航中断了初始导航,Remix 会取消之前的获取请求,以确保只有最新的请求继续进行。

      ¥Remix's Approach: Remix manages client-side navigation the same way. When a link is clicked within a Remix application, it initiates fetch requests for each loader tied to the target URL. If another navigation interrupts the initial navigation, Remix cancels the previous fetch requests, ensuring that only the latest requests proceed.

  • 浏览器表单提交:如果你在浏览器中发起表单提交,然后快速再次提交另一个表单,浏览器将忽略第一个提交,只处理最新的提交。

    ¥Browser Form Submission: If you initiate a form submission in a browser and then quickly submit another form again, the browser disregards the first submission, processing only the latest one.

    • Remix 的方法:Remix 在使用表单时模仿此行为。如果提交了一个表单,并且在第一个表单完成之前发生了另一个提交,Remix 将取消原始的获取请求。然后,它会等待最新提交完成,然后再触发页面重新验证。

      ¥Remix's Approach: Remix mimics this behavior when working with forms. If a form is submitted and another submission occurs before the first completes, Remix cancels the original fetch requests. It then waits for the latest submission to complete before triggering page revalidation again.

并发提交和重新验证

¥Concurrent Submissions and Revalidation

虽然标准浏览器在导航和表单提交时每次只能处理一个请求,但 Remix 提升了这种行为。与导航不同,useFetcher 可以同时处理多个请求。

¥While standard browsers are limited to one request at a time for navigations and form submissions, Remix elevates this behavior. Unlike navigation, with useFetcher multiple requests can be in flight simultaneously.

Remix 旨在高效处理向服务器 action 提交的多个表单以及并发的重新验证请求。它确保一旦有新数据可用,状态就会立即更新。然而,Remix 也通过避免在其他 action 引入竞争条件时提交过时数据来防范潜在的陷阱。

¥Remix is designed to handle multiple form submissions to server actions and concurrent revalidation requests efficiently. It ensures that as soon as new data is available, the state is updated promptly. However, Remix also safeguards against potential pitfalls by refraining from committing stale data when other actions introduce race conditions.

例如,如果正在进行三个表单提交,其中一个已完成,Remix 会立即使用该数据更新 UI,而无需等待其他两个表单提交完成,从而确保 UI 保持响应速度和动态性。当剩余的提交完成后,Remix 会继续更新 UI,确保显示最新数据。

¥For instance, if three form submissions are in progress, and one completes, Remix updates the UI with that data immediately without waiting for the other two so that the UI remains responsive and dynamic. As the remaining submissions finalize, Remix continues to update the UI, ensuring that the most recent data is displayed.

为了帮助理解一些可视化效果,以下是图表中使用的符号的图例:

¥To help understand some visualizations, below is a key for the symbols used in the diagrams:

  • |:开始提交

    ¥|: Submission begins

  • ✓:操作完成,数据重新验证开始

    ¥✓: Action complete, data revalidation begins

  • ✅ 重新验证的数据将提交到 UI

    ¥✅: Revalidated data is committed to the UI

  • ❌ 请求已取消

    ¥❌: Request cancelled

submission 1: |----✓-----✅
submission 2:    |-----✓-----✅
submission 3:             |-----✓-----✅

然而,如果后续提交的重新验证先于先前提交完成,Remix 会丢弃先前的数据,以确保只有最新的信息才会反映在 UI 中。

¥However, if a subsequent submission's revalidation completes before an earlier one, Remix discards the earlier data, ensuring that only the most up-to-date information is reflected in the UI.

submission 1: |----✓---------❌
submission 2:    |-----✓-----✅
submission 3:             |-----✓-----✅

由于提交 (2) 的重新验证比提交 (1) 开始得晚,到达得早,因此提交 (1) 的请求将被取消,并且只有提交 (2) 的数据会提交到 UI。它是稍后请求的,因此它更有可能包含 (1) 和 (2) 的更新值。

¥Because the revalidation from submission (2) started later and landed earlier than submission (1), the requests from submission (1) are canceled and only the data from submission (2) is committed to the UI. It was requested later, so it's more likely to contain the updated values from both (1) and (2).

数据过时的可能性

¥Potential for Stale Data

你的用户不太可能遇到这种情况,但在基础设施不一致的极少数情况下,用户仍有可能看到过时的数据。即使 Remix 取消了对过时数据的请求,它们最终仍会到达服务器。在浏览器中取消请求只会释放该请求的浏览器资源,无法执行 "赶上进度" 操作并阻止其访问服务器。在极少数情况下,已取消的请求可能会在中断 action 的重新验证生效后更改数据。考虑此图:

¥It's unlikely your users will ever experience this, but there are still chances for the user to see stale data in very rare conditions with inconsistent infrastructure. Even though Remix cancels requests for stale data, they will still end up making it to the server. Cancelling a request in the browser simply releases browser resources for that request, it can't "catch up" and stop it from getting to the server. In extremely rare conditions, a canceled request may change data after the interrupting action's revalidation lands. Consider this diagram:

     👇 interruption with new submission
|----❌----------------------✓
       |-------✓-----✅
                             👆
                  initial request reaches the server
                  after the interrupting submission
                  has completed revalidation

用户现在查看的数据与服务器上的数据不同。请注意,此问题极为罕见,并且也存在于默认浏览器行为中。在任何网络和服务器基础架构上,初始请求到达服务器的时间都晚于第二个请求的提交和重新验证,这种可能性是不可预料的。如果你的基础设施担心这个问题,你可以在表单提交时发送时间戳,并编写服务器逻辑以忽略过时的提交。

¥The user is now looking at different data than what is on the server. Note that this problem is both extremely rare and exists with default browser behavior, too. The chance of the initial request reaching the server later than both the submission and revalidation of the second is unexpected on any network and server infrastructure. If this is a concern in with your infrastructure, you can send time stamps with your form submissions and write server logic to ignore stale submissions.

示例

¥Example

在组合框等 UI 组件中,每次按键都可以触发网络请求。管理如此快速、连续的请求可能很棘手,尤其是在确保显示的结果与最近的查询匹配时。然而,Remix 可以自动处理这一挑战,确保用户看到正确的结果,而无需开发者对网络进行细致的管理。

¥In UI components like combo boxes, each keystroke can trigger a network request. Managing such rapid, consecutive requests can be tricky, especially when ensuring that the displayed results match the most recent query. However, with Remix, this challenge is automatically handled, ensuring that users see the correct results without developers having to micromanage the network.

import type { LoaderFunctionArgs } from "@remix-run/node"; // or cloudflare/deno
import { json } from "@remix-run/node"; // or cloudflare/deno

export async function loader({
  request,
}: LoaderFunctionArgs) {
  const { searchParams } = new URL(request.url);
  const cities = await searchCities(searchParams.get("q"));
  return json(cities);
}

export function CitySearchCombobox() {
  const fetcher = useFetcher<typeof loader>();

  return (
    <fetcher.Form action="/city-search">
      <Combobox aria-label="Cities">
        <ComboboxInput
          name="q"
          onChange={(event) =>
            // submit the form onChange to get the list of cities
            fetcher.submit(event.target.form)
          }
        />

        {/* render with the loader's data */}
        {fetcher.data ? (
          <ComboboxPopover className="shadow-popup">
            {fetcher.data.length > 0 ? (
              <ComboboxList>
                {fetcher.data.map((city) => (
                  <ComboboxOption
                    key={city.id}
                    value={city.name}
                  />
                ))}
              </ComboboxList>
            ) : (
              <span>No results found</span>
            )}
          </ComboboxPopover>
        ) : null}
      </Combobox>
    </fetcher.Form>
  );
}

应用只需知道如何查询数据以及如何渲染数据;Remix 处理网络。

¥All the application needs to know is how to query the data and how to render it; Remix handles the network.

结论

¥Conclusion

Remix 为开发者提供了一种直观的、基于浏览器的方法来管理网络请求。通过镜像浏览器行为并在需要时进行增强,它简化了并发、重新验证和潜在竞争条件的复杂性。无论你构建的是简单的网页还是复杂的 Web 应用,Remix 都能确保你的用户交互流畅、可靠且始终保持最新。

¥Remix offers developers an intuitive, browser-based approach to managing network requests. By mirroring browser behaviors and enhancing them where needed, it simplifies the complexities of concurrency, revalidation, and potential race conditions. Whether you're building a simple webpage or a sophisticated web application, Remix ensures that your user interactions are smooth, reliable, and always up to date.

Remix v2.17 中文网 - 粤ICP备13048890号