资讯动态

TanStack Query(Solid Query)查询取消指南:AbortSignal、手动取消与状态回滚

发布时间:2026/9/10 11:58:39 来源:尧图企业网站定制
TanStack QuerySolid Query查询取消指南AbortSignal、手动取消与状态回滚【免费下载链接】query Powerful asynchronous state management, server-state utilities and data fetching for the web. TS/JS, React Query, Solid Query, Svelte Query and Vue Query.项目地址: https://gitcode.com/GitHub_Trending/qu/query本文面向使用tanstack/solid-query的开发者系统讲解查询取消机制TanStack Query 如何借助标准AbortSignal自动取消过期与失活查询、各 HTTP 客户端fetch、axios、XMLHttpRequest、graphql-request如何接入信号以及如何通过queryClient.cancelQueries提供用户可操作的手动取消并深入取消参数silent/revert的底层语义与状态回滚原理。读完本文你将能够在 Solid 应用中安全地编写支持取消的查询函数同时理解取消背后的源码级实现。文档来源与 Solid 专属写法本仓库中 docs/framework/solid/guides/query-cancellation.md 是 Solid 框架的查询取消指南。该文件的 front matter 通过ref: docs/framework/react/guides/query-cancellation.md指向 React 侧的同一主题文档并以一组replace规则自动生成 Solid 版本其中最关键的一条就是把useQuery({替换为useQuery(() ({。这是因为 Solid 的useQuery采用响应式 Accessorgetter签名查询选项必须包裹在一个函数中供 Solid 的响应式系统按需重算。以 useQuery.ts 的实现为证它内部实际执行了useBaseQuery(createMemo(() options()), QueryObserver, queryClient)即对选项再做一次记忆化求值。因此下文所有示例一律沿用本文档的 Solid 写法const todosQuery useQuery(() ({ queryKey: [todos], queryFn: async ({ signal }) { // 查询函数体 }, }))查询取消机制的核心原理TanStack Query 会为每一次查询请求创建独立的AbortSignal实例并把它注入查询函数上下文的signal字段。当查询因为过期out-of-date或不再被任何组件观察inactive而需要取消时这个 signal 会被置为 aborted查询函数内部就能监听并响应取消实现自动取消。从源码看其真实载体在 query-core 的 Query.fetch 中每次 fetch 会先new AbortController()query.ts#L441为该次请求分配独立信号signal 并不是直接塞进上下文而是通过Object.defineProperty定义了一个带 getter 的signal属性query.ts#L446-L454。只有当查询函数实际读取signal时内部标记#abortSignalConsumed才会被置为true这个是否消费过信号的标记直接决定了查询在卸载时的行为见下节默认行为当retryer执行取消回调时会调用abortController.abort()query.ts#L545真正让注入到fetch、axios等底层请求中的 signal 生效。这一机制带来的最大好处是你完全可以使用熟悉的async/await语法书写查询函数同时免费获得自动取消能力无需自己维护任何取消状态。取消后的状态回滚revert查询取消不同于普通请求中断它需要把查询自身的 state 恢复原状。源码中这由两处配合完成在发起 fetch 前Query 先把当前 state 暂存为#revertState this.statequery.ts#L522createRetryer的onCancel回调中如果收到的CancelledError带revert标记就用#revertState覆盖当前状态并把fetchStatus重置为idlequery.ts#L538-L546。也就是说取消查询的结果是让查询像从未发起这次 fetch 一样。默认行为卸载并不等于取消默认情况下查询在其 Promise 解析完成前卸载或失活并不会被取消。这是文档明确强调的默认行为当组件在请求完成前卸载、但查询函数没有消费signal时请求继续跑完解析出的数据会进入缓存。若此后再次挂载该组件、且查询尚未被垃圾回收数据将立即可用反之如果查询函数消费了AbortSignal读取了signal那么一旦 Promise 被取消例如底层 fetch 被 abortQuery 本身也必须取消其结果表现为状态回滚到先前状态。这一按需消费的分流决策在源码中有明确实现。查看 Query.removeObserver当最后一个 observer 被移除时if ( this.#abortSignalConsumed || (this.state.fetchStatus paused this.state.status pending) ) { this.#retryer.cancel({ revert: true }) } else { this.#retryer.cancelRetry() }若 signal 已被消费说明查询函数把请求与信号绑定了就调用cancel({ revert: true })中断请求并回滚状态若信号从未被读取说明底层请求无法被中断则调用cancelRetry()阻止后续重试但放行当前请求完成后把数据写入缓存。实战一与fetch集成fetch原生支持AbortSignal直接把上下文中的signal透传即可。特别值得注意的是同一个 signal 可以被同时传给多个请求下面示例先用它请求/todos列表再把它分发给每个明细请求一次取消即可同时中断所有关联请求const todosQuery useQuery(() ({ queryKey: [todos], queryFn: async ({ signal }) { const todosResponse await fetch(/todos, { // Pass the signal to one fetch signal, }) const todos await todosResponse.json() const todoDetails todos.map(async ({ details }) { const response await fetch(details, { // Or pass it to several signal, }) return response.json() }) return Promise.all(todoDetails) }, }))实战二与axios集成axios v0.22.0 及以上直接传递 signal自 axios v0.22.0 起axios 请求配置原生支持signal选项直接透传即可无需任何额外桥接import axios from axios const todosQuery useQuery(() ({ queryKey: [todos], queryFn: ({ signal }) axios.get(/todos, { // Pass the signal to axios signal, }), }))axios v0.22.0 以下借助 CancelToken 桥接旧版 axios 不支持signal只能使用其CancelToken体系。此时需要监听 TanStack Query 注入的 signal 的abort事件再调用source.cancel()手动取消 axios 请求import axios from axios const todosQuery useQuery(() ({ queryKey: [todos], queryFn: ({ signal }) { // Create a new CancelToken source for this request const CancelToken axios.CancelToken const source CancelToken.source() const promise axios.get(/todos, { // Pass the source token to your request cancelToken: source.token, }) // Cancel the request if TanStack Query signals to abort signal?.addEventListener(abort, () { source.cancel(Query was cancelled by TanStack Query) }) return promise }, }))这里使用可选链signal?.addEventListener是对不同环境下 signal 可用性的防御性写法代码语义等价于TanStack Query 发起取消时 → 浏览器触发 abort 事件 → axios 的 CancelToken 收到source.cancel()→ 底层请求终止Promise 变为 rejected → 查询进入取消回滚流程。实战三与XMLHttpRequest集成XMLHttpRequest同样没有原生 signal 支持需要手动桥接手动创建一个 Promise并在abort事件中调用oReq.abort()并reject()const todosQuery useQuery(() ({ queryKey: [todos], queryFn: ({ signal }) { return new Promise((resolve, reject) { const oReq new XMLHttpRequest() oReq.addEventListener(load, () { resolve(JSON.parse(oReq.responseText)) }) signal?.addEventListener(abort, () { oReq.abort() reject() }) oReq.open(GET, /todos) oReq.send() }) }, }))这是接入任何不支持 signal 但提供 abort 能力的请求器的通用桥接范式监听 signal 的abort事件 → 调用请求器自身的取消方法 →reject掉 Promise。实战四与graphql-request集成graphql-request提供了两种注入 signal 的方式分别对应不同版本。graphql-request v4.0.0 及以上在 request 方法中传入将 signal 作为request方法调用的一部分传入下文query指 GraphQL 请求文档字符串const client new GraphQLClient(endpoint) const todosQuery useQuery(() ({ queryKey: [todos], queryFn: ({ signal }) { client.request({ document: query, signal }) }, }))graphql-request v4.0.0 以下在构造函数中传入低版本request方法不接收 signal需要把 signal 放入GraphQLClient构造参数让该客户端发出的所有请求共享该信号const todosQuery useQuery(() ({ queryKey: [todos], queryFn: ({ signal }) { const client new GraphQLClient(endpoint, { signal, }) return client.request(query, variables) }, }))两种方式的取舍值得注意在构造函数里传 signal则该客户端与一次查询绑定通常应在查询函数内部创建客户端实例在request方法里传 signal 则更精细、可复用同一客户端。手动取消让用户终止慢请求当某个请求耗时过长时你通常希望允许用户点击取消按钮主动中断请求。此时使用queryClient.cancelQueries({ queryKey })它会取消匹配的查询并将其状态回滚到之前的状态并且只要查询函数消费过signalTanStack Query 还会连带取消底层 Promise即真实中断网络请求const todosQuery useQuery(() ({ queryKey: [todos], queryFn: async ({ signal }) { const resp await fetch(/todos, { signal }) return resp.json() }, })) const queryClient useQueryClient() return ( button onClick{(e) { e.preventDefault() queryClient.cancelQueries({ queryKey: [todos] }) }} Cancel /button )cancelQueries的底层实现在 queryClient.ts它把调用方传入的cancelOptions与默认值{ revert: true }合并然后对每个匹配的 query 执行query.cancel(...)而 Query.cancel 会调用当前 retryer 的cancel()返回一个已被吞掉 rejection 的 Promise因此你可以在按钮回调里安全地await它。对应地Retryer.cancel 会构造一个带取消参数的CancelledError并 reject 内部 Promise同时触发onCancel回调——这正构成了上面signal abort 状态回滚的源头。Cancel Optionssilent 与 revert从文档和类型定义CancelOptions见 queryClient.ts#L16来看取消操作支持一个可选的 options 对象用于精细控制取消行为// Cancel specific queries silently await queryClient.cancelQueries({ queryKey: [posts] }, { silent: true })silent?: boolean设为true时抑制CancelledError向 observers例如onError回调及相关的通知传播并返回 retry 的 Promise 而不是抛出 rejection默认值为false。revert?: boolean设为true时将查询的状态data 与 status恢复为本次请求发起前的状态把fetchStatus置回idle并且仅在没有先前数据时才抛出错误默认值为true这也是cancelQueries内部合并的默认值。在源码层面CancelledError 正是把这两个选项作为自身属性保存随后由 Query.fetch 的 retryer onCancel 依据error.revert决定是否应用#revertState由 observer 层依据silent决定是否向上抛出。换言之silent影响的是取消这件事要不要打扰组件层revert影响的是查询数据要不要恢复到请求前。运行时兼容性提示自动取消依赖标准AbortController/AbortSignalAPI。该 API 在现代浏览器与主流运行时中均已内置但如果你的运行环境如某些旧版 WebView 或较老的 SSR 环境不支持就需要自行引入 polyfill社区有多种可用的abortcontrollerpolyfill 实现。仓库内部的 query-core 在主进程中同样直接使用原生new AbortController()query.ts#L441因此整个取消链路对运行时的要求是一致的。限制与注意事项需要特别留意的是查询取消存在一些边界限制。官方文档该段内容经由 front matter 机制继承自 React 侧指南明确指出取消在配合 Suspense 类 hooks如useSuspenseQuery、useSuspenseQueries、useSuspenseInfiniteQuery使用时并不生效。在 Solid 语境下Suspense 集成走的是另一条路径Solid Query 通过createResource在内部构建资源见 useBaseQuery.ts组件只需在Suspense边界内读取todosQuery.data即可挂起无需单独的useSuspenseQuery变体。因此若你的场景既需要 Suspense 挂起又依赖取消能力请结合 Suspense 指南 验证具体行为同时在 查询函数指南 与 queries 指南 中也会看到 signal 在 queryFn 上下文中的完整定位。小结与延伸阅读查询取消是 TanStack Query 异步状态管理中的基础能力之一其设计可概括为三条主线自动注入每次 fetch 由 Query 创建独立AbortController以懒求值 getter 方式暴露signal并据此判断查询函数是否消费了信号按需中断未消费 signal 的请求在组件卸载后继续完成并缓存数据已消费 signal 的请求则被取消并把状态回滚到#revertState全链路接入无论fetch、axios、XMLHttpRequest还是graphql-request都可通过直接透传或事件桥接完成信号对接必要时用cancelQueries提供面向用户的取消入口并通过silent/revert微调行为。在动手前请先通读 完整的 Solid 查询取消文档以及其底本的 React 版本并结合以下仓库内资料加深理解实现证据Query.fetch / removeObserver / cancel、Retryer 与 CancelledError、QueryClient.cancelQueriesSolid 适配层useQuery、useBaseQuery 的 createResource 集成相关指南查询函数、查询与 Suspense、Queries 基础。【免费下载链接】query Powerful asynchronous state management, server-state utilities and data fetching for the web. TS/JS, React Query, Solid Query, Svelte Query and Vue Query.项目地址: https://gitcode.com/GitHub_Trending/qu/query创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价