拓冰建站拓冰建站
首页 / 资讯中心 / 正文

使用 useTimeoutFn 构建可控制的 setTimeout:Airi 项目 VueUse 定时器组合式函数实战

使用 useTimeoutFn 构建可控制的 setTimeoutAiri 项目 VueUse 定时器组合式函数实战【免费下载链接】airi Self hosted, you-owned Grok Companion, a container of souls of waifu, cyber livings to bring them into our worlds, wishing to achieve Neuro-samas altitude. Capable of realtime voice chat, Minecraft, Factorio playing. Web / macOS / Windows supported.项目地址: https://gitcode.com/GitHub_Trending/ai/airiuseTimeoutFn是 VueUse 提供的setTimeout响应式封装它把原生定时器转换为带有start、stop、isPending控制的 Stoppable 组合式函数是 Vue 3 应用中实现可取消、可重启动、可自动清理的一次性延时任务的标准方案。本文以 Airi 仓库中的 useTimeoutFn 参考文档 为主体结合仓库内真实的 OIDC Token 定时刷新实现讲解它的完整用法、参数语义与源码级实战模式。useTimeoutFn 是什么useTimeoutFn是 VueUse Animation动画分类下的组合式函数官方定位一句话即可概括Wrapper forsetTimeoutwith controls——即带控制能力的setTimeout封装。与原生setTimeout相比它解决的核心痛点有三个可响应式控制返回start()/stop()方法可以随时停止计时、随时重新开始无需手动管理timer句柄与clearTimeout。可自动清理组合式函数在组件卸载或 effect scope 销毁时自动清除挂起的定时器避免内存泄漏。可响应式时长延时参数支持传入ref或 getterMaybeRefOrGetternumber时长变化可以直接驱动下一次计时。在 SKILL.md 的函数速查表中useTimeoutFn与useInterval、useIntervalFn、useNow、useRafFn、useTimeout、useTimestamp等同列于 Animation 分类调用规则为AUTO——即只要场景适用Vue.js / Nuxt 项目中出现一次性延时需求就可以直接使用优先于手写setTimeout。基础用法关联文档给出的最小用法如下import { useTimeoutFn } from vueuse/core const { isPending, start, stop } useTimeoutFn(() { /* ... */ }, 3000)调用后立即开始 3 秒倒计时倒计时结束后执行回调并通过解构返回三个核心控制项返回项类型说明isPendingRefboolean计时是否仍在进行中响应式start() void启动或重启计时器stop() void停止并清除计时器其中isPending是响应式ref可以直接参与模板渲染或watch逻辑。整体返回类型为StoppableVueUse 中「可停止」组合式函数的通用返回契约与useIntervalFn、useRafFn等保持一致因此写法与配套 API 在整个 VueUse 生态中是统一、可迁移的。停止与重启的语义stop()会立即清除当前挂起的定时器回调不会再执行isPending变为false。start()会基于当前延时重新开始倒计时等价于「重启」而非「续跑」。这在「用户交互后重置超时」「重新登录后重新排程」等场景中非常关键Airi 的 auth store 正是依赖这一语义完成 Token 刷新的动态重排下文详述。Options 参数详解useTimeoutFn的第三个参数为UseTimeoutFnOptions关联文档的类型声明完整给出了两个选项export interface UseTimeoutFnOptions { /** * Start the timer immediately * * default true */ immediate?: boolean /** * Execute the callback immediately after calling start * * default false */ immediateCallback?: boolean }选项类型默认值语义immediatebooleantrue是否在调用组合式函数时立即启动计时器immediateCallbackbooleanfalse调用start()时是否立即执行一次回调再开始计时immediate: false 的典型场景当你不希望组合式函数一创建就启动计时而是由后续业务事件决定何时启动时设置immediate: false并持有start()句柄const { start, stop } useTimeoutFn(callback, 3000, { immediate: false }) // 业务条件满足后再启动 start()这正是 Airi 仓库中 OIDC Token 刷新调度的用法见下一节。immediateCallback: true 的典型场景immediateCallback默认关闭开启后start()会先同步执行一次回调再启动计时。适合「先立即做一次再定时做」的模式例如进入页面立即拉取一次数据并同时挂起轮询计时。函数签名与类型约束关联文档给出的完整类型声明export type UseTimeoutFnReturnCallbackFn extends AnyFn Stoppable ParametersCallbackFn | [] export declare function useTimeoutFnCallbackFn extends AnyFn( cb: CallbackFn, interval: MaybeRefOrGetternumber, options?: UseTimeoutFnOptions, ): UseTimeoutFnReturnCallbackFn值得注意的两点泛型约束CallbackFn extends AnyFn回调函数类型被泛型保留且返回类型StoppableParametersCallbackFn | []表明——如果回调接收参数start在启动计时时也会透传这些参数。interval: MaybeRefOrGetternumber延时既可以是普通数字也可以是refnumber或返回数字的 getter这让「动态调整延时」无需重建组合式函数。仓库实战Airi 中的 OIDC Token 定时刷新Airi 仓库中useTimeoutFn最具代表性的真实用例位于 packages/stage-ui/src/stores/auth.ts——用一次性定时器实现 OIDC Access Token 的「到期前自动刷新」调度const { start: startRefreshTimer, stop: stopRefreshTimer } useTimeoutFn( () { void useAuthStore().refreshTokenNow() }, refreshDelayMs, { immediate: false }, ) function scheduleTokenRefresh(expiresInSeconds: number): void { stopRefreshTimer() // Guard against missing/invalid lifetimes (e.g. token response omitted // expires_in). useTimeoutFn with NaN/0 delay would fire immediately // and spin a refresh loop — skip scheduling instead. if (!Number.isFinite(expiresInSeconds) || expiresInSeconds 0) return // Refresh at 80% of lifetime refreshDelayMs.value expiresInSeconds * 0.8 * 1000 startRefreshTimer() }这段代码几乎完整覆盖了useTimeoutFn的全部核心能力是理解该 API 的最佳源码教材immediate: false按需启动auth store 初始化时不会立刻启动计时器只有成功获取到 Token 后由scheduleTokenRefresh/completeSignIn等业务路径显式调用startRefreshTimer()才开始倒计时。响应式延时MaybeRefOrGetter实战refreshDelayMs是一个ref(0)每次重新排程时先stopRefreshTimer()清除旧计时再更新refreshDelayMs.value expiresInSeconds * 0.8 * 1000取 Token 生命周期的 80% 作为刷新点最后startRefreshTimer()。延时参数的响应式设计让「动态排程」只需改一个 ref无需重建组合式函数。stop()幂等重排每次调度前无条件stopRefreshTimer()保证「旧计时未触发时被取消、新计时从零开始」的重启语义配合start()即构成完整的「取消重排」流程。边界防御源码注释明确指出——useTimeoutFn在延时为NaN或 0时会立即触发回调若 Token 响应缺失expires_in字段直接调度会形成刷新死循环。因此先以Number.isFinite(expiresInSeconds) expiresInSeconds 0守卫拦截非法值再调度。自动清理effect scope 语义源码在 auth.ts 的注释中写明「Uses useTimeoutFn for automatic cleanup on store teardown」——该 store 基于 Pinia setup 语法创建useTimeoutFn的计时器绑定在组件/effect scope 生命周期上store 销毁时无需手动clearTimeout即可自动清理而在登出、刷新失败等显式退出路径中clearAuthState()仍会调用stopRefreshTimer()主动停止挂起的刷新见 auth.ts实现「自动清理 显式停止」双保险。仓库对vueuse/core的依赖声明于 packages/stage-ui/package.json版本通过catalog:由 pnpm workspace 统一管理。与相关组合式函数的选型对比useTimeoutFn常与同分类下的几个函数混淆选型时可参考下表函数语义适用场景与 useTimeoutFn 的差异useTimeoutFn一次性延时后执行回调到期触发一次的动作Token 刷新、超时跳转本文主角直接执行回调useTimeout延时后ready变为true需要「是否已到点」的响应式布尔状态参考 useTimeout.md不执行回调只翻转响应式状态底层基于useTimeoutFn可传controls暴露start/stop/isPendinguseIntervalFn周期性重复执行回调轮询、心跳、循环任务Airi 中用于 UI 轮询见 provider.ts一次性 vs 周期性的区别useRafFn每帧执行回调动画循环、滚动同步Airi 中用于虚拟滚动驱动见 use-virtualizer-scroll.ts基于requestAnimationFrame适合高帧率任务选型口诀只执行一次回调 →useTimeoutFn只要一个「到点」的响应式标志 →useTimeout需要周期性执行 →useIntervalFn需要逐帧驱动 →useRafFn。四者返回的start/stop/pause/resume/isPending控制面高度一致学习成本极低。常见问题与最佳实践小结如何避免提前触发将immediate设为false在真正需要时再调用start()若延时依赖异步数据如从接口拿到的有效期务必在拿到合法数值后再启动。如何防御非法延时参考 Airi 的做法调度前用Number.isFinite()与 0校验NaN、0、负数都会导致回调立即执行可能引发刷新/请求死循环。如何实现「取消重排」先stop()再更新延时 ref 最后start()三步组合即得到幂等的重新调度能力。如何避免内存泄漏信任 effect scope 的自动清理同时在显式退出路径登出、组件卸载前的主动停止中调用stop()。回调参数透传若回调声明了参数start(...args)会把这些参数在启动时透传给回调。综上useTimeoutFn用极小的 API 面两个选项、三个返回值、一个泛型签名完整解决了 Vue 应用中一次性延时任务的可控性、响应式与资源安全三大问题Airi 的 OIDC Token 刷新调度即为该模式的可直接复用的工程化范本。【免费下载链接】airi Self hosted, you-owned Grok Companion, a container of souls of waifu, cyber livings to bring them into our worlds, wishing to achieve Neuro-samas altitude. Capable of realtime voice chat, Minecraft, Factorio playing. Web / macOS / Windows supported.项目地址: https://gitcode.com/GitHub_Trending/ai/airi创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
分享:

看完干货,该让你的企业上线了

免费需求沟通 · 48 小时内出具建站方案 · 河南本地可上门