实时订阅

第 5 课 · 共 6 课 约 8 分钟

先查后订,靠序号续传,不丢不重;连接是要占计算分钟的,所以只给有人在看的界面。

本课目标

读完这一课,你将能够

  • 说出订阅解决什么问题,什么时候不该订阅
  • 用「先查后订」和序号续传做到不丢不重
  • 用 live() 做一个实时视图,用 watch() 只收变化

为什么要订阅

看板要跟着数据走。有两种笨办法:每隔几秒重新查一次,大多数时候什么都没变,白白占着资源;或者干脆不更新,用户看到的是旧数据。订阅反过来:连接一直开着,数据一变,服务端就把变化推给你。

订阅有几件事在任何系统里都一样。你订的是一个对象集:对象加入、属性变化、被删除,你都会收到;服务端给不出增量时,会叫你重新加载整个集合;连接会断,所以客户端必须能重连。

官方 OSDK 通过 v2 WebSocket 订阅。AIDC 浏览器 SDK 仍用 SSE,也就是服务端推送的 HTTP 连接。本节的 live() 与 watch() 是兼容写法。SSE 用递增序号续传:断线后带上最后收到的序号,服务端从那里接着推。

  1. 01先查取一页当前值,同时拿到序号 seq
  2. 02再订从 seq 开始,中间的变化不会漏
  3. 03推送每次变化推来一条:哪个对象、什么操作、新值
  4. 04断线记着最后收到的序号
  5. 05续传带序号回来,不丢不重

live() 与 watch()

语义 SDK 和数据 SDK 各给一个入口。live() 给你一个一直保持最新的对象集,watch() 更底层,只给变化本身。

live():一直最新的全集watch():只要变化
你得到什么先是全量,之后每次变化都给你最新的全集只有变化:一条一条,带序号
适合看板、列表、大屏:拿到就渲染自己维护状态、写日志、触发别的动作
断线与落后自动按序号续上,连接状态由 onStatus 告诉你同样按序号续传;落后太多时收到 onReset,要重新查询
import { semantic, data } from "/developer/sdk/v1/aidc.js";

// 车间大屏:先是全量,之后每次变化回调一次最新的全集
const view = semantic.objects("workOrder").where({ status: "running" }).sort("-dueAt").live({
  onUpdate(objects, change) { render(objects); },
  onStatus({ connection }) { badge(connection); },   // connecting / live / reconnecting / paused / closed
});
// 页面离开时:view.close();

// 只要变化:先查,再从查到的序号订阅
const page = await data.table("workOrder").list({ where: { status: "running" }, limit: 50 });
const sub = data.watch({ types: ["workOrder"], after: page.seq }, {
  onChange(c) { /* { seq, type, pk, op: "upsert" | "delete", props, origin: "sync" | "action" | "edit" | "import", at } */ },
  onReset(seq) { /* 落后太多:重新查询 */ },
});

每条变化带着 origin:sync 是数据源同步,action 是 Action,edit 是直接修改,import 是批量导入。看板想突出「刚被人改过」的对象,看它就行。应用只会收到它看得见、并且订阅了的类型。

订阅不是备份。落后太多时你会收到 onReset,这时不要指望逐条补齐,重新查询一次就好。

连接是要占计算分钟的:什么时候该用

一条挂着的连接不是没有成本的:页面隐藏超过 1 分钟,连接自动断开(状态变成 paused),切回来带着序号续上;挂着的时间按时长计入应用的计算分钟,缺省每月 2000。所以订阅只给有人正在看的界面。

有人盯着屏幕没人看也要反应
用什么live() 或 watch()工作流的 change 触发器
连接页面开着才有,隐藏超过 1 分钟自动断开没有长连接:数据一变才触发,每条变化只触发一次
示例工厂车间大屏:某条产线正在运行的工单检验结果为 fail:建缺陷、暂停工单、通知质量负责人

隔一段时间才要做的事,比如每天早上提醒可能延期的订单,用工作流的 schedule 触发器,不用订阅。设计时先问一句:是不是数据一变才需要做?是,就用 change;只有人在看的时候才需要,才用订阅。

  • 先订后查会漏掉中间的变化:永远先查,再从查到的序号订阅。
  • op: "delete" 不是错误:它表示这个对象没了,界面上把它去掉。
  • 忘了 close():页面卸载后连接还挂着,一直占计算分钟。
  • 收到 onReset 就重新查询,不要硬接后面的变化。

要点

  • 订阅是连接不断、数据一变就推送;断线按序号续传,不丢不重。
  • 永远先查、再从查到的序号订阅;落后太多(onReset)就重新查询。
  • live() 给一个一直最新的对象集,watch() 只给变化,每条带 origin。
  • 连接挂着按时长计入计算分钟,页面隐藏超过 1 分钟自动断开;订阅只给有人在看的界面。
  • 没人看也要反应,用工作流的 change 触发器;隔一段时间才做,用 schedule。

练一练

为车间大屏设计订阅

用示例工厂的工单练习,前两项写在纸上,第三项需要一个终端。

写出大屏的订阅顺序:先做什么,序号从哪里来,断线之后怎么办,收到 onReset 怎么办。

小测

选一个答案,马上看解析。

Q1为什么要「先查、再从查到的序号订阅」?

Q2设备振动超阈值要自动建维保工单,半夜没人看任何页面。该怎么做?

Q3一个订阅的连接对应用有什么成本?

延伸阅读