第07章 - 动作详解(三):等待、延时与滚动
自动化流程的成败,很大程度上取决于「时机」——页面还没加载完就去点击,必然失败。本章讲解三类控制节奏的动作:wait(等待元素出现)、wait_gone(等待元素消失)、sleep(固定延时),以及 scroll(滚动)。其中 wait 系列是编写健壮工作流的关键。
7.1 为什么需要等待
桌面与 Web 应用的响应是异步的:点击一个按钮后,新页面/弹窗需要时间加载。如果工作流不加等待,紧接着就去操作还没出现的元素,就会「找不到元素」而失败。
robotgo-flow 提供了两种截然不同的等待思路:
- 智能等待(
wait/wait_gone):轮询检测某个元素是否出现/消失,一旦满足条件立即继续,最长等到超时。这是推荐方式——既不会白等,也不会过早操作。 - 固定延时(
sleep):无条件等待固定的秒数。简单粗暴,但要么等太久浪费时间,要么等太短仍失败。仅在无法用元素判断状态时使用。
7.2 wait:等待元素出现
wait 会轮询查找某个模板,直到它出现或超时。两种写法:
# 简写:使用全局默认超时(settings.element_timeout)
- wait: "templates/loading_done.png"
# 完整写法:为这次等待单独指定超时(秒)
- wait: {template: "templates/popup.png", timeout: 30}
对应源码:wait 字段既接受字符串(模板路径,使用默认超时),也接受包含 template 与 timeout 的映射(parseWait 解析)。执行时调用 eng.WaitForElement(template, timeout)。
7.2.1 等待的内部机制
引擎的 WaitForElement 通过 matcher.go 中的轮询实现:每 200ms 检测一次目标模板是否能被找到,直到找到(成功返回)或达到超时(失败)。查找本身遵循「窗口内优先、全屏回退」的策略(详见第 10 章)。
7.2.2 典型用法
wait 通常紧跟在会触发页面变化的动作之后:
steps:
- name: "打开并等待加载"
actions:
- open_url: "https://example.com/dashboard"
- wait: "templates/dashboard_ready.png" # 等仪表盘加载出来
- name: "点击并等待弹窗"
actions:
- click: "templates/btn_new.png"
- wait: {template: "templates/dialog.png", timeout: 15} # 等弹窗,最多 15 秒
- click: "templates/dialog_ok.png"
7.3 wait_gone:等待元素消失
wait_gone 与 wait 相反,它等待某个元素消失:
- wait_gone: "templates/loading.png"
最典型的场景是等待加载动画消失:很多页面加载时会显示一个 loading 转圈图标,加载完成后消失。用 wait_gone 等这个图标消失,就能准确判断「加载完成」。
内部通过 WaitForElementGone 实现:轮询查找模板,直到 FindElement 返回错误(即找不到该元素,意味着它已消失)或超时。
steps:
- name: "提交并等待完成"
actions:
- click: "templates/btn_submit.png"
- wait_gone: "templates/loading_spinner.png" # 等 loading 消失
- wait: "templates/success_msg.png" # 再等成功提示出现
wait 与 wait_gone 配合使用,可以精确地卡准异步操作的起止时机。
7.4 sleep:固定延时
sleep 无条件等待固定的秒数,支持小数:
- sleep: 2.5 # 等待 2.5 秒
- sleep: 0.5 # 等待半秒
对应 Action 的 Sleep float64 字段。sleep.go 中的实现直接使用 Go 的 time.Sleep,等待 seconds * time.Second。
7.4.1 什么时候用 sleep
sleep 应作为「最后手段」,仅在无法用元素状态判断时机时使用,例如:
- 目标程序没有明显的「加载完成」视觉标志;
- 需要等待一个纯粹的时间过程(如动画播放、倒计时);
- 在两个快速操作之间留出极短的缓冲。
7.4.2 sleep 与 wait 的取舍
| 对比项 | wait / wait_gone |
sleep |
|---|---|---|
| 等待依据 | 元素出现/消失 | 固定时间 |
| 效率 | 条件满足立即继续 | 必须等满设定时间 |
| 健壮性 | 高,适应快慢网络 | 低,时间写死 |
| 适用场景 | 有明确视觉标志 | 无法用元素判断时 |
经验法则:能用 wait/wait_gone 就不要用 sleep。sleep 写多了,工作流会变得又慢又脆。
7.5 scroll:滚动
scroll 用于滚动页面或列表,值是一个整数,正数向下、负数向上,数值代表滚动量:
# 向下滚动
- scroll: 500
# 向上滚动
- scroll: -300
对应 Action 的 Scroll int 字段。scroll.go 的执行逻辑:
amount > 0→eng.ScrollDown(amount);amount < 0→eng.ScrollUp(-amount);amount == 0→ 什么也不做。
7.5.1 滚动的底层行为
引擎的 ScrollDown/ScrollUp:
- 普通模式:直接调用
robotgo.ScrollDir一次性滚动; - 人类模式(启用滚动抖动):把滚动量拆成 1~3 单位的小步分块执行,步间有 30~120ms 间隔,并有 20% 概率在结束后轻微反向回滚 1~2 单位,模拟真实滚动的不精确性。
7.5.2 典型用法:滚动到目标再操作
模板匹配只能找到当前屏幕上可见的元素。如果目标在页面下方看不见,需要先滚动让它进入视野:
steps:
- name: "滚动找到并点击"
actions:
- scroll: 800 # 向下滚动
- wait: "templates/target_button.png" # 等目标进入视野
- click: "templates/target_button.png"
对于很长的页面,可能需要多次滚动逐步查找。
7.6 综合示例:处理异步加载列表
结合本章的动作,处理一个「加载列表 → 等待完成 → 滚动浏览 → 点击项」的流程:
steps:
- name: "打开列表页"
actions:
- open_url: "https://example.com/items"
- wait_gone: "templates/loading.png" # 等加载动画消失
- wait: "templates/list_container.png" # 等列表容器出现
- name: "滚动并选择"
actions:
- scroll: 600 # 向下滚动
- sleep: 0.5 # 给渲染留点时间
- wait: "templates/target_row.png" # 等目标行出现
- click: "templates/target_row.png" # 点击目标行
7.7 常见问题
| 问题 | 可能原因 | 解决方法 |
|---|---|---|
| 频繁「等待超时」 | 超时设得太短、模板不准 | 增大 timeout 或 element_timeout;检查模板 |
| 明明加载完了还在等 | wait_gone 的模板选得不好 |
选一个确实会消失的元素(如 loading 图标) |
| 滚动后找不到元素 | 滚动量不够、渲染延迟 | 增大滚动量或分多次滚动,配合短 sleep |
| 工作流整体很慢 | 滥用 sleep |
用 wait/wait_gone 替换固定延时 |
7.8 小结
本章我们讲解了控制节奏的四种动作:wait 智能等待元素出现(每 200ms 轮询,可指定超时)、wait_gone 等待元素消失(适合等加载动画结束)、sleep 固定延时(最后手段)、scroll 滚动(正下负上,人类模式分块执行)。核心经验是:优先用智能等待,慎用固定延时。下一章,我们讲解一组专为浏览器场景设计的动作。