不去问 — 轮询比「浪费」更糟的地方
5 分钟目标MCP 客户端课程最后一篇。订阅、被通知、然后再读。轮询费资源只是次要的——真正的问题是:两次询问之间值变了两次,中间那一下永远看不到。
到第四篇为止,想知道最新的值就得去问——每秒问一次,或者靠人刷新。
订阅
client.onResourceUpdated((uri) {
// The notification says only that it changed. Read to learn what to.
seen.add(uri);
stdout.writeln('notified: $uri');
});
await client.subscribeResource('desk://waiting');然后让值真的变一下。
await client.callTool('desk.admit', {'count': 1});subscribed to desk://waiting
notified: desk://waiting
after the notification, reading gives {waiting: 2}
notifications received: 1通知只带 uri
处理器收到的是一个 uri。变成几个人不会来。服务端第六篇讲了为什么不带值;在接收这一侧,它的意思就是 你必须去读。
final now = await client.readResource('desk://waiting');多走一趟看着吃亏,可这样永远是对的。不管来几条通知、什么顺序,读的那一刻的值 就是最新的。
轮询真正的问题
拒绝轮询的理由通常是成本。每秒敲一次服务端是浪费。
那是次要的。真正的问题是,你看不到两次询问之间。
09:00:00 轮询 → waiting 3
09:00:00.4 进来两个 → waiting 1
09:00:00.7 来了一个 → waiting 2
09:00:01 轮询 → waiting 2画面从 3 到了 2。它没看到曾经是 1 的那一刻。 如果只要最终值,无所谓;可要是有「归零就报警」这类规则,那条规则永远不会触发。
把间隔调小也消不掉,只是变窄。而越窄,浪费越大。
结束就退订
await client.unsubscribeResource('desk://waiting');画面关掉就退订。不退,服务端会一直往一个不存在的对象发。
真板那篇里的 ESP32 把这件事放进了 画面定义里 —— onReady 订阅,onDestroy 解除。生命周期写在画面上,客户端就忘不掉。
校验 —— 是轮询就失败
grep -q 'notified: desk://waiting' captures/s5.txt || die "no notification arrived"
grep -q 'notifications received: 1' captures/s5.txt || die "wrong notification count"
grep -q 'reading gives {waiting: 2}' captures/s5.txt || die "the read after the notification was stale"
# Polling would show up as a timer; the claim is that there is none.
grep -qE 'Timer|periodic' bin/step5.dart && die "it is polling, not subscribing"代码里出现 Timer 或 periodic 就失败。因为只看结果,轮询和订阅长得一模一样 —— 两个都会在屏上显示 2。
数通知条数也是同样的道理。0 说明没来,好几条说明重复订阅了。
五篇结束了
step1 connected to Course 1.0.0
step2 tools: desk.admit
resources: ui://desk, desk://waiting
step3 admit 1 -> {waiting: 2}
admit 99 -> isError=true "only 2 waiting"
step4 ui://desk — 546 B, 6 lines, type "page", title "Desk"
step5 notifications received: 1 · after the notification, reading gives {waiting: 2}
5 steps · each checked against its own claim · driven against the course server没有 mock 服务端。 五级全都真的把服务端课程里做的那台服务端起起来再接。所以两条轨一旦走偏,就会在这儿卡住——第三篇里真的卡了一次。
自己跑一遍
cd content/sample/course-client
dart pub get
bash verify.sh # 五级全跑
dart run bin/step5.dart可以带走的
onResourceUpdated+subscribeResource— 先挂处理器再订阅- 通知之后去读 — 通知只带 uri
- 禁止轮询的检查 — 结果看起来一样,所以要看代码
下一个轨道
服务端和客户端都立住了。下一段是画面本身——一坨 JSON 是怎么变成一块画面的。
运行示例
makemind-academy/course_client/git clone https://github.com/makemind-academy/course_client cd course_client dart pub get (cd course-server && dart pub get) dart run bin/step5.dart