JSBridge 不是发消息就完了:runJavaScript、Timeout 和 Callback Lost

JSBridge 不是发消息就完了:runJavaScript、Timeout 和 Callback Lost

项目地址:github.com/lichenyang5...

本文是 MiniAppRuntime-Harmony 系列的第五篇,重点讲回调执行、超时和 callback lost。

1. 能调用成功,不代表 JSBridge 完成了

很多 JSBridge Demo 到这里就结束了:

text 复制代码
H5 调 ArkTS
ArkTS 回调 H5

但是实际框架不能只考虑成功路径。

还要考虑:

  • ArkTS 没有回调怎么办?
  • H5 页面已经超时了,ArkTS 结果才回来怎么办?
  • requestId 找不到 callback 怎么办?
  • JSON 字符串里有引号,runJavaScript 拼坏怎么办?
  • BridgeController 里直接拼 JS 字符串,会不会越来越乱?

所以这个阶段做了两件事:

text 复制代码
抽出 BridgeCallbackExecutor
补充 TIMEOUT / CALLBACK_LOST

2. 为什么要抽出 BridgeCallbackExecutor

一开始,BridgeController 可以直接调用:

ts 复制代码
this.controller.runJavaScript(...)

但是这样会让 Controller 变重。

它既要接收消息,又要分发请求,还要拼 JS 字符串,还要处理回调异常。

所以我把回调逻辑抽出来:

text 复制代码
BridgeCallbackExecutor

它只做一件事:

把 BridgeResponse 安全送回 H5。

职责包括:

  • 序列化响应;
  • 安全转义 JSON 字符串;
  • 拼接 H5 回调函数;
  • 调用 runJavaScript
  • 记录成功或失败日志。

BridgeController 只需要:

ts 复制代码
await this.callbackExecutor.sendResponse(response)

这样职责就清楚了。

3. 为什么 JSON 要安全转义

ArkTS 回调 H5 时,本质上是执行一段 JS:

js 复制代码
window.__myascf_on_native_response__('{...}')

如果 response 里面有引号:

json 复制代码
{
  "message": "hello 'world'"
}

就可能导致脚本字符串拼接出错。

所以不能简单拼字符串。

更稳妥的方式是,把 response JSON 再作为 JS 字符串参数处理,确保引号、换行等字符不会破坏脚本。

这类细节放在 CallbackExecutor 里,比散落在各个业务 handler 里更安全。

4. H5 为什么需要 timeout

H5 调用 ArkTS,本质是异步请求。

异步请求一定要有超时。

否则如果 ArkTS 因为某种原因没有回调,H5 Promise 会一直 pending。

所以 window.myascf.send 支持 timeout:

js 复制代码
window.myascf.send("ui.showToast", {
  message: "hello"
}, {
  timeout: 5000
})

H5 侧 callback map 保存的不只是 resolve / reject,还包括 timer:

js 复制代码
callbacks.set(requestId, {
  resolve: resolve,
  reject: reject,
  timer: timer,
  action: action,
  createdAt: Date.now()
})

如果超时前收到响应:

text 复制代码
clearTimeout(timer)
delete callback
resolve / reject

如果超时后还没收到响应:

text 复制代码
delete callback
reject TIMEOUT

5. 什么是 CALLBACK_LOST

一种常见情况是:

text 复制代码
H5 请求超时
→ callback 被删除
→ ArkTS 后来才回调
→ H5 找不到 requestId

这就是 callback lost。

这不是致命错误。

正确做法是:

js 复制代码
if (!callback) {
  console.warn('CALLBACK_LOST', response.requestId)
  return
}

不要让页面崩掉。

不要影响其他请求。

只记录日志。

6. H5 回调生命周期

完整生命周期如下:

text 复制代码
send 开始
→ 创建 requestId
→ 创建 Promise
→ 创建 timeout timer
→ callbacks.set(requestId, callback)
→ 发送请求给 ArkTS
→ 收到响应
→ clearTimeout
→ callbacks.delete(requestId)
→ code === 0 ? resolve : reject

如果超时:

text 复制代码
send 开始
→ callbacks.set
→ timeout 到期
→ callbacks.delete
→ reject TIMEOUT
→ 后续响应回来时 CALLBACK_LOST

7. Demo 里怎么验证

页面可以提供几个按钮:

text 复制代码
正常调用 ui.showToast
参数错误调用 ui.showToast
未知 action 调用 debug.unknownAction
极短 timeout 测试

例如:

js 复制代码
window.myascf.send("ui.showToast", {
  message: "timeout test"
}, {
  timeout: 1
})

如果链路足够快,timeout 可能不稳定触发。

这时可以后续加一个 mock delay handler 来专门测试。

8. 这一阶段的意义

到这里,JSBridge 已经不仅是"能发消息"。

它开始具备框架应有的稳定性:

  • 成功响应;
  • 参数错误;
  • 未知 action;
  • 内部异常;
  • 超时;
  • callback lost;
  • 安全回调;
  • 职责分层。

后续可以继续扩展两个方向:

第一,新增更多 API,比如 clipboard。

第二,做一个调试面板,把每次调用的 requestId、action、耗时、结果展示出来。

项目地址:

text 复制代码
https://github.com/lichenyang5/MiniAppRuntime-Harmony
相关推荐
xiaofeichaichai1 小时前
Vite配置实战
前端
zhanghaofaowhrql1 小时前
为CSDN博客编辑器安装自定义CSS主题,打造个性化写作界面
前端·css·编辑器
懒得不想起名字2 小时前
flutter 使用go_router 和 page_transition定义页面动画
前端
多加点辣也没关系2 小时前
JavaScript|第6章:流程控制语句
开发语言·前端·javascript
csj502 小时前
前端基础之《React(11)—state声明式》
前端·react.js
程序员晓琪2 小时前
深入解析:从 Webpack 到 Vite,多入口多端项目的工程化迁移实战
前端·webpack·vite
涛涛ing2 小时前
Vue 3.6 还没正式发布,但前端的方向已经被它定下来了
前端
阿新聊ai2 小时前
Claude Code 为什么总不懂你的项目?缺一张「项目地图」
前端·后端·面试
暗冰ཏོ2 小时前
Spring Boot + Vue3 订单支付模块技术实现:支付单、回调验签、幂等处理与余额扣减
前端·spring boot·后端·vue·微信支付·支付宝支付