Turbo Drive 2.0: Should a Global Timeout Configuration Be Added?
0 reputation · 25 Jul 2021, 21:09 UTC
0 reputation · 25 Jul 2021, 21:09 UTC
Developers using Turbo Drive often need to limit how long a page visit can wait for a server response, especially when dealing with unreliable backends or long‑running endpoints.
The current implementation relies on the Fetch API and provides no default timeout; a visit proceeds until the network layer signals failure or the server replies. To impose a limit, one must listen to turbo:before-fetch-request, create an AbortController, and tie a setTimeout to abort the request, as shown in the official documentation.
This manual approach raises concerns about boilerplate, potential memory leaks if controllers are not cleaned up, and the lack of a centralized configuration that could be versioned or overridden per environment.
What are the trade‑offs of adding a timeout option to Turbo Drive’s configuration? How would it interact with existing turbo:before-fetch-request and turbo:visit‑cancelled events? Should the timeout be defined globally, per‑visit, or scoped to specific frames?
A global timeout option is a reasonable addition, but only as a default with per-visit overrides, not as a single fixed value. The manual AbortController pattern works today and is the documented approach; the real question is whether centralizing it buys enough to justify new API surface and new semantics for cancellation events.
Confirmed: Turbo Drive's visits use the Fetch API, which has no built-in timeout. Without an AbortSignal, a request waits until the network stack or server terminates it. The current escape hatch is listening to turbo:before-fetch-request, creating an AbortController, and aborting from a setTimeout.
Likely, but not confirmed for any specific release: a built-in option would be implemented as a Turbo-owned AbortController, which means it must define precedence against user-supplied signals. Verify the exact event names and configuration surface against your installed @hotwired/turbo version before relying on them — they are version-sensitive.
turbo:before-fetch-request, you get double-abort or ambiguity. The API needs a documented rule: user signal wins, or signals are combined (any abort cancels).AbortError. Today, cancellation via turbo:before-fetch-request means "don't send this request," which is different from "the request expired mid-flight." A built-in timeout should dispatch a distinguishable outcome so developers can tell user cancellation from timeout.Global default plus per-visit override mirrors how Turbo already layers behavior: data-turbo-method and similar attributes override global config per element. A data-turbo-timeout attribute on links and forms, plus an option on Turbo.visit, fits the existing mental model. Frame-scoped timeouts add configuration complexity for marginal benefit — frames that need special handling can set the attribute on their own links and forms.
Manual setups can leak timers if the timeout is not cleared when the fetch completes — that concern is legitimate. But it is not evidence a built-in option is leak-free; implementation quality still matters. If you stay with the manual pattern, clear the timer in a fetch completion handler, not just on abort.
Also worth checking: modern browsers ship AbortSignal.timeout(ms), which makes the manual pattern nearly trivial and reduces the pressure for a built-in option:
document.addEventListener("turbo:before-fetch-request", (event) => {
event.detail.fetchOptions.signal = AbortSignal.timeout(10000);
});Verify AbortSignal.timeout support against your target browsers before adopting it.
Yes, a global default timeout is defensible — but only with per-visit overrides, a documented signal-precedence rule, a distinct timeout event, and defined post-timeout UX. Without those four decisions, the option trades a small amount of boilerplate for ambiguous behavior. Prototype the manual pattern first and observe how your app handles a mid-flight abort (page state, progress bar, event sequence); that experiment defines what the built-in feature would actually need to do.
Use comments to ask for clarification. Post a solution as an answer.
No question comments on this page.