任务必须是可描述的对象
不要把自动化逻辑写成一串连续点击。每个任务应该有 id、type、payload、status、attempts、createdAt 和 lastError。 这样你才能暂停、恢复、去重、排序和排查失败原因。
{
"id": "task_20260519_001",
"type": "SEND_REPLY",
"payload": {"threadId": "abc", "templateId": "price"},
"status": "queued",
"attempts": 0,
"lastError": null
}
用状态机约束流程
常见状态包括 queued、running、waiting_page、retrying、done、failed、paused。插件的 popup 只负责展示和操作状态, 真正执行逻辑放在 background 或 content script 协作完成。状态变化要写日志,避免出现“插件没反应”却不知道卡在哪一步。
关键原则:任务执行器每次只做一个小动作,然后把结果写回队列。不要在一个函数里做完整流程。
重试不是简单 setTimeout
重试要区分错误类型。页面元素不存在可能需要等待 DOM;接口 429 需要退避;账号状态异常应该立即暂停;业务校验失败则不应重试。 对可重试错误使用指数退避,并设置最大次数,防止插件在后台无限循环。
- transient:网络抖动、页面加载慢,可重试。
- rate_limit:频率限制,延迟更久并降低并发。
- validation:输入不合法,不重试,直接失败。
- account_risk:账号或页面状态异常,暂停队列等待人工处理。
IndexedDB 比 localStorage 更适合队列
队列需要存结构化对象、查询状态、按时间排序和写入日志,IndexedDB 更稳。localStorage 适合少量配置,不适合高频任务状态。 如果任务涉及敏感内容,存储层还要做字段裁剪和过期清理,只保存执行所需的最小数据。
队列是否支持暂停、恢复和清空。
任务是否有唯一 id 和幂等处理。
失败是否保存可读错误,而不是只写 console。
执行速率是否可配置,避免对页面和账号造成压力。