- 人工智能
- AI Agent
- MCP 服务
- 工具调用
- GUI 自动化
- 语音
- 数字人
- 交互助手
【免费下载链接】airi
💖🧸 自托管、归你拥有的 Grok 风格 AI 伴侣与 waifu / 赛博生命灵魂容器,目标是接近 Neuro-sama 的高度;支持实时语音聊天、Minecraft 和 Factorio 游玩,支持 Web / macOS / Windows。
useTimeoutPoll是 VueUse 提供的一个定时轮询组合式函数,其核心语义是:每一次轮询回调执行完毕后,才重新计时调度下一次执行,从而天然避免setInterval在回调为异步任务时出现的"任务堆积"与"重叠执行"问题。在 airi 这类大量使用 Vue 3 组合式 API、并且依赖@vueuse/core的 Monorepo 项目中(锁定版本@vueuse/core@14.4.0,见 pnpm-lock.yaml),它适合用于状态同步、配置刷新、轻量级心跳等场景。读完本文,你将掌握useTimeoutPoll的完整 API、配置项语义、与useIntervalFn/useTimeoutFn的取舍,以及如何在真实组件中落地可暂停、可恢复的轮询逻辑。
为什么需要 useTimeoutPoll
传统的setInterval(fn, delay)有一个经典陷阱:如果fn是耗时的异步任务(如发起网络请求),任务的实际执行时间可能超过delay,导致下一次回调在上一次任务尚未完成时就被触发,出现并发重叠、状态错乱甚至请求风暴。
useTimeoutPoll的文档给出了明确的核心语义:
Use timeout to poll something. It will trigger callback after last task is done.
即"在最后一次任务完成之后,才触发下一次回调"。它在内部基于setTimeout而非setInterval调度:每次调用传入的fn(无论同步还是异步),等到fn返回的 Promise 完成(settle)之后,再重新安排interval毫秒后的下一次执行。这样任意时刻最多只有一个轮询任务在飞行中,天然保证了串行与安全。
在 airi 仓库中,同样可以看到手动setInterval轮询带来的样板代码,例如 apps/component-calling/src/utils/xsai-testing.ts 中const interval = setInterval(() => {...})的手写写法——需要自行管理句柄、清理与暂停恢复。这正是useTimeoutPoll这类组合式函数要消除的重复劳动。
基本用法
从@vueuse/core引入后,只需传入回调函数与轮询间隔:
import { useTimeoutPoll } from '@vueuse/core' import { ref } from 'vue' const count = ref(0) async function fetchData() { await new Promise(resolve => setTimeout(resolve, 1000)) count.value++ } // 只有上一次 fetchData 完成之后,才会开始下一次 1000ms 计时 const { isActive, pause, resume } = useTimeoutPoll(fetchData, 1000)在这个示例中:
fetchData是一个耗时约 1 秒的异步任务;- 轮询间隔为
1000ms; - 由于
useTimeoutPoll等待上一次任务完成后才重新计时,实际触发节奏约为"任务耗时 + 间隔",即大约每 2 秒一次,而不是固定 1 秒一次; - 解构出的
isActive、pause、resume提供了对轮询生命周期的完全控制(对应Pausable返回类型,下详)。
注意:回调参数的类型是fn: () => Awaitable<void>,即同步返回void或返回 Promise 的函数均可;间隔参数interval: MaybeRefOrGetter<number>,意味着它可以是普通数字、ref或 getter 函数(详见下文"动态间隔")。
完整类型声明与配置项
关联文档给出了完整的类型声明,这是理解全部行为的钥匙:
export interface UseTimeoutPollOptions { /** * 是否立即启动计时器 * * @default true */ immediate?: boolean /** * 调用 resume 后是否立即执行回调 * * @default false */ immediateCallback?: boolean } export declare function useTimeoutPoll( fn: () => Awaitable<void>, interval: MaybeRefOrGetter<number>, options?: UseTimeoutFnOptions, ): Pausable参数说明
| 参数 | 类型 | 说明 |
|---|---|---|
fn | () => Awaitable<void> | 每次轮询要执行的回调,可以是同步函数或返回 Promise 的异步函数 |
interval | MaybeRefOrGetter<number> | 轮询间隔(毫秒)。支持普通数字、ref<number>或() => numbergetter,便于动态调整 |
options | UseTimeoutPollOptions | 可选配置,见下表 |
配置项说明
| 配置项 | 默认值 | 含义 |
|---|---|---|
immediate | true | 是否在创建组合式函数后立即启动计时器。设为false后,轮询默认处于暂停状态,需要手动调用resume()才开始 |
immediateCallback | false | 调用resume()恢复轮询时,是否立即执行一次回调,而不是等待下一个间隔。适合"暂停后恢复时要立刻刷新一次最新状态"的场景 |
返回的 Pausable 接口
函数返回类型为Pausable,这是一个在 VueUse 计时器家族中广泛复用的接口(useIntervalFn的UseIntervalFnReturn同样定义为Pausable,见 references/useIntervalFn.md),包含三个成员:
isActive: Ref<boolean>:当前轮询是否处于活跃(运行中)状态,可直接用于模板或watch;pause(): void:暂停轮询。下一次已调度的setTimeout会被清除,且已暂停状态被持久保留;resume(): void:恢复轮询。默认等待一个完整间隔后再触发,若配置immediateCallback: true则会立即执行一次回调。
与 useIntervalFn、useTimeoutFn 的取舍
在 airi 的 VueUse 技能知识库中,这三个函数分属不同定位(完整目录见 .agents/skills/vueuse-functions/SKILL.md):
| 函数 | 类别 | 调度方式 | 是否等待异步回调完成 | 适用场景 |
|---|---|---|---|---|
useIntervalFn | Animation | setInterval | 否 | 高频、确定性的轻量任务(如动画、定时累加) |
useTimeoutFn | Animation | setTimeout(一次性) | 是(一次任务) | 延迟执行单次任务,返回Stoppable |
useTimeoutPoll | Utilities | setTimeout(循环) | 是 | 轮询远端状态、异步刷新、心跳 |
三者共享immediate/immediateCallback两个配置项(useTimeoutFn对应start/stop与Stoppable返回,见 references/useTimeoutFn.md)。选择建议:
- 回调是纯同步且很快的(如本地计数器),
useIntervalFn足够且开销更低; - 回调发起网络请求、读写存储等异步操作,务必选择
useTimeoutPoll,避免重叠; - 只需执行一次延迟任务,用
useTimeoutFn。
进阶实战:动态间隔与状态刷新
场景一:动态轮询间隔
由于interval接受MaybeRefOrGetter<number>,可以依据某个状态实时改变轮询频率。例如在 AI 伴侣类应用中,根据"是否处于活跃对话"决定状态刷新频率:
import { useTimeoutPoll } from '@vueuse/core' import { computed, ref } from 'vue' const isInConversation = ref(false) const intervalGetter = computed(() => (isInConversation.value ? 2000 : 15000)) const { isActive, pause, resume } = useTimeoutPoll( async () => { // 例如拉取最新状态快照 await refreshSnapshot() }, intervalGetter, )间隔变化会在下一次调度时自然生效,无需手动重启轮询。
场景二:暂停后立即刷新
当用户重新回到页面或窗口获得焦点时,通常希望立刻获得最新状态而不是干等一个间隔:
const { resume } = useTimeoutPoll(fetchStatus, 5000, { immediate: false, immediateCallback: true, // resume 后立即执行一次 fetchStatus }) onFocus(() => resume()) onBlur(() => pause())场景三:与 useFetch 等异步工具协同
fn支持 async 函数,因此可以自然地包裹任意异步逻辑:
import { useFetch, useTimeoutPoll } from '@vueuse/core' const { data, isFinished, execute } = useFetch('/api/status', { immediate: false }) useTimeoutPoll(() => execute(), 10_000)useTimeoutPoll会在execute()返回的 Promise 完成后才安排下一次轮询,避免对同一接口的并发请求。
仓库中的轮询生命周期实践参考
airi 仓库中的 packages/input-gamepad-vueuse/src/use-standard-gamepad.ts 展示了与Pausable完全一致的生命周期管理模式,可作为手写轮询组合式函数时的对照参考:
- 通过
isActive(shallowRef)维护轮询运行状态; pause()内先判断isActive再停止,避免重复操作(第 79-85 行);resume()内校验支持性与活跃状态后再启动(第 87-93 行);- 使用
tryOnMounted(resume)在挂载时启动、tryOnScopeDispose在作用域销毁时清理(第 99-103 行)。
这些边界处理(幂等的 pause/resume、作用域自动清理)也正是useTimeoutPoll内建提供的——组合式函数随组件卸载自动释放定时器,无需手动clearTimeout。
使用建议与注意事项
- 后台节流:与所有基于
setTimeout/setInterval的方案一样,页面处于后台或隐藏标签页时,浏览器可能降低定时器频率。若需要精确的固定节奏,应结合useDocumentVisibility在页面恢复可见时触发一次resume(配合immediateCallback)刷新状态。 - 错误处理:
fn内部若抛错或返回 rejected Promise,轮询调度本身不受影响(下一次仍会按计划触发),但建议在回调内自行try/catch包裹网络请求,避免未捕获异常污染控制台。 - SSR 兼容性:VueUse 计时器系列对服务端渲染安全,定时器仅在客户端实际运行。
- 调用规则:在 airi 的技能体系中,
useTimeoutPoll的调用规则为AUTO(见 SKILL.md),即开发 Vue 功能时,凡是"异步轮询"需求都应优先考虑它,而不是手写setInterval。
- 人工智能
- AI Agent
- MCP 服务
- 工具调用
- GUI 自动化
- 语音
- 数字人
- 交互助手
【免费下载链接】airi
💖🧸 自托管、归你拥有的 Grok 风格 AI 伴侣与 waifu / 赛博生命灵魂容器,目标是接近 Neuro-sama 的高度;支持实时语音聊天、Minecraft 和 Factorio 游玩,支持 Web / macOS / Windows。
相关推荐
airi 项目中的 VueUse useTimeoutPoll:实现"任务完成后"再轮询的定时拉取方案
airi 项目中的 VueUse useTimeoutPoll:实现"任务完成后"再轮询的定时拉取方案 导读 useTimeoutPoll 是 VueUse 提
AI 应用人工智能大模型数字人AI Agent语音前端后端桌面应用移动开发即时通讯3D渲染Terminus 项目 LarePass 客户端安装与账号接入指南:从多平台下载到 Olares ID 全流程
Terminus 项目 LarePass 客户端安装与账号接入指南:从多平台下载到 Olares ID 全流程 LarePass 是 Terminus 开源项目
云原生后端运维Goav开发者必读:项目架构分析与扩展开发最佳实践
Goav开发者必读:项目架构分析与扩展开发最佳实践 Goav作为Golang绑定FFmpeg的重要项目,为开发者提供了在Go语言环境中处理音视频的强大能力。本文
AI 应用人工智能大模型数字人AI Agent语音前端后端桌面应用移动开发即时通讯3D渲染
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考