H5 如何调用 ArkTS:JavaScriptProxy、requestId 和 Promise 回调

H5 如何调用 ArkTS:JavaScriptProxy、requestId 和 Promise 回调

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

本文是 MiniAppRuntime-Harmony 系列的第三篇,重点讲 H5 到 ArkTS 的最小通信边界。

1. JSBridge 的本质是什么

JSBridge 听起来很抽象。

但拆开看,它其实只做两件事:

text 复制代码
H5 把请求发给 Native
Native 把结果回给 H5

问题在于,这两个动作都不是同步的。

H5 调用:

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

它希望拿到的是一个 Promise:

js 复制代码
window.myascf
  .send("ui.showToast", { message: "hello from h5" })
  .then(res => console.log(res))
  .catch(err => console.error(err))

所以我们要设计一套协议,让异步请求和异步响应可以对应起来。

关键就是:requestId。

2. 请求协议

H5 发给 ArkTS 的请求长这样:

json 复制代码
{
  "requestId": "req_1700000000000_1",
  "action": "ui.showToast",
  "params": {
    "message": "hello from h5"
  }
}

字段不多,但每个都很重要。

requestId 用来匹配响应。

action 表示要调用哪个能力。

params 是参数。

如果没有 requestId,H5 同时发出两个请求时,就无法知道哪个响应属于哪个请求。

3. H5 侧 send 方法

H5 侧的 send 大致做这些事:

js 复制代码
var callbacks = new Map()

window.myascf = {
  send: function(action, params, options) {
    return new Promise(function(resolve, reject) {
      var requestId = createRequestId()

      var request = {
        requestId: requestId,
        action: action,
        params: params || {}
      }

      callbacks.set(requestId, {
        resolve: resolve,
        reject: reject,
        action: action,
        createdAt: Date.now()
      })

      window.MyASCFNative.postMessage(JSON.stringify(request))
    })
  }
}

这里有一个 callback map。

它的作用是保存每一次请求对应的 Promise 回调。

等 ArkTS 回来时,再根据 requestId 找到它。

4. ArkWeb 注入对象

ArkTS 侧通过 JavaScriptProxy 向 H5 注入一个对象:

ts 复制代码
Web({
  src: $rawfile('web/index.html'),
  controller: this.controller
})
.javaScriptProxy({
  object: this.nativeProxy,
  name: 'MyASCFNative',
  methodList: ['postMessage'],
  controller: this.controller
})

H5 侧就可以调用:

js 复制代码
window.MyASCFNative.postMessage(JSON.stringify(request))

这里的 postMessage 是 ArkTS 暴露出来的方法。

5. JavaScriptProxy 只做转发

JavaScriptProxy 不应该写业务逻辑。

它只负责把消息交给 BridgeController。

ts 复制代码
export class JavaScriptProxy {
  constructor(private bridgeController: BridgeController) {}

  postMessage(message: string): void {
    this.bridgeController.handleMessage(message)
  }
}

这是一条很重要的边界。

如果在 Proxy 里直接判断 action,那么它很快就会变得混乱。

Proxy 是入口,不是业务层。

6. ArkTS 返回响应

ArkTS 返回给 H5 的响应格式如下:

json 复制代码
{
  "requestId": "req_1700000000000_1",
  "code": 0,
  "message": "success",
  "data": {
    "echoAction": "ui.showToast"
  }
}

H5 暴露一个全局回调:

js 复制代码
window.__myascf_on_native_response__ = function(responseText) {
  var response = JSON.parse(responseText)
  var callback = callbacks.get(response.requestId)

  if (!callback) {
    console.warn('CALLBACK_LOST', response.requestId)
    return
  }

  callbacks.delete(response.requestId)

  if (response.code === 0) {
    callback.resolve(response)
  } else {
    callback.reject(response)
  }
}

ArkTS 通过 runJavaScript 调用这个函数。

7. 为什么要小心字符串转义

ArkTS 回调 H5 时,本质上是在执行一段 JS 字符串。

比如:

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

如果 JSON 里有引号、换行或特殊字符,脚本就可能拼坏。

所以项目里抽出了 BridgeCallbackExecutor,统一负责:

  • JSON 序列化;
  • 参数安全转义;
  • 拼接 JS;
  • 执行 runJavaScript;
  • 记录日志。

这样 BridgeController 就不需要关心字符串细节。

8. 这一阶段的意义

到这里,我们还没有实现真正的 Toast。

但是通信边界已经成立了:

text 复制代码
H5 Promise
→ requestId
→ callback map
→ JavaScriptProxy
→ BridgeController
→ runJavaScript
→ H5 Promise resolve / reject

这一步很重要。

因为后面的 Dispatcher、Registry、Biz、Imp 都建立在这个边界之上。

项目地址:

text 复制代码
https://github.com/lichenyang5/MiniAppRuntime-Harmony
相关推荐
用户461169657286 分钟前
网站 HTTPS 不稳定?证书排查指南
前端
n876517 分钟前
Playwright Java WEB UI 自动化实现框架与逻辑
java·前端·ui
keyipatience19 分钟前
TypeScript学习(1)
前端·javascript·学习·typescript·html·css3
xifangge20251 小时前
基于 JavaScript 实现 CNC 数控切削参数与螺纹公差纯前端核算引擎
开发语言·前端·javascript
wangruofeng1 小时前
2026 Web 技术选型指南,AI 时代如何选择适合项目的技术栈
前端·aigc·ai编程
honkun61 小时前
vue 表格组件 vxe-table 修改滚动条样式,修改滚动条样式,统一宽度、颜色与自动隐藏
前端·javascript·vue.js
爱勇宝2 小时前
研究数字孪生的这半年,做了个 Demo,也有了一些新想法
前端·程序员
不可能片场2 小时前
工程实践 一个缓存把选片从十分钟压到一分钟
前端·electron
前端小万2 小时前
探索副业五个月,最终以放弃告终
前端·后端·面试
FRing032 小时前
JavaScript入门到精通二十四(正则表达式、元字符、修饰符)
开发语言·前端·javascript·正则表达式