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
相关推荐
雪芽蓝域zzs1 小时前
(六)打包优化 + Nginx 部署完整配置 + 项目收尾
前端·vue.js
研☆香1 小时前
聊一聊前端的常见字 关键字
开发语言·前端·javascript
东风破_2 小时前
JWT 1:从一个登录请求开始,理解 React 项目里的 API 层与 Mock
前端·后端
东风破_2 小时前
JWT 3:为什么 Token 要放进 Authorization?Axios 拦截器到底解决了什么?
前端·后端
东风破_2 小时前
JWT 5:路由守卫是什么?把整个 JWT 登录鉴权流程串起来
前端·后端
东风破_2 小时前
JWT 2:HTTP 是无状态的,为什么登录成功后还要给 Token?
前端·后端
东风破_2 小时前
JWT 4:Zustand 到底解决了什么?为什么登录状态要放进 Store?
前端·后端
IT_陈寒2 小时前
Vite动态导入差点让我秃头,原来问题出在这
前端·人工智能·后端
白泽_hunter3 小时前
项目里最常见的 5 个 this 指向坑:从规则到实战彻底讲透
前端
宿6743 小时前
vue3-pinia
前端·vue.js