HarmonyOS 7 互动卡片请求超时:为什么不能立刻再提交一次
客户端没有收到回执,不等于服务端没有完成操作。请求超时后立刻换一个操作标识重试,最容易把一次点击变成两次提交。应该先进入结果未知状态,再查询原操作的回执。
适配范围
示例不提供权威回执服务。not_done只有在后端能保证原请求不会再晚到执行时才能使用;否则仍需同key幂等。客户端重启后需要恢复未决操作,不能把内存状态清空就允许再次执行。
案例一
超时但实际成功:收到相同key的done回执后转成功,不产生新操作。
案例二
权威确认尚未执行:允许用原key重试。查询暂时无记录不一定是not_done,存在写入延迟时必须继续保持unknown。
可以运行的实现
export class ActionOutcome { state = 'idle'; key = null; start(key){if(this.state==='pending'||this.state==='unknown')throw Error('unresolved');this.key=key;this.state='pending';} timeout(){if(this.state!=='pending')throw Error('not_pending');this.state='unknown';} reconcile(key,outcome){if(key!==this.key||this.state!=='unknown')return false;if(!['done','not_done'].includes(outcome))throw Error('outcome');this.state=outcome==='done'?'done':'retryable';return true;} }复现与断言
把实现与以下测试放进同一个example.mjs,执行node example.mjs。断言失败会报错,全部通过正常退出。这些断言已经在本地Node.js执行;它们不代表SDK编译、真机接口调用或性能实测已经通过。
import assert from 'node:assert/strict'; const action=new ActionOutcome();action.start('k1');action.timeout(); assert.throws(()=>action.start('k2'),/unresolved/); assert.equal(action.reconcile('other','done'),false);assert.equal(action.state,'unknown'); assert.equal(action.reconcile('k1','done'),true);assert.equal(action.state,'done'); const retry=new ActionOutcome();retry.start('x');retry.timeout();retry.reconcile('x','not_done');assert.equal(retry.state,'retryable');retry.start('x');assert.equal(retry.state,'pending');方案取舍
把超时当失败最省事,却把传输状态和业务结果混为一谈。unknown状态虽然让状态机多一条分支,却能保留不确定性。服务端幂等与回执查询比单纯界面防抖更重要。
接入时别漏掉这些边界
示例不提供权威回执服务。not_done只有在后端能保证原请求不会再晚到执行时才能使用;否则仍需同key幂等。客户端重启后需要恢复未决操作,不能把内存状态清空就允许再次执行。
复用时把输入获取与纯逻辑分开:界面、系统事件和服务请求负责取得真实输入,独立模块负责给出判断或保存状态。保留上述两个案例作为回归测试,并对失败原因记录最少必要日志,不记录文件内容、账号凭据或其它敏感数据。升级SDK后重新核对支持设备、接口版本与错误行为,而不是只改目标版本号。
官方资料
HarmonyOS 官方能力入口
示例验证环境为Node.js,验证范围是应用侧逻辑。本文不将示例当作系统接口接入代码;具体接口的支持设备、最低版本和调用方式,以所用SDK对应的官方文档为准。