摆脱异步烦恼的响应式数据
Source: Dev.to
现代前端开发常常变成与异步代码的搏斗:
async/await- 订阅
- 依赖数组
- 记忆化
- 竞争条件
- 不必要的重新计算
如果你只需要描述你的数据,让系统自行处理其余工作,会怎样?
这正是 rs‑x 背后的理念。
为了演示,我做了一个小 demo,用来实时跟踪国际空间站(ISS)的位置。
👉 在线演示:
思路
演示程序轮询公共 ISS API,并将数据作为 reactive model(响应式模型)的一部分公开。
⚠️ 注意: 如果大量用户同时打开演示,公共 ISS API 可能会因速率限制而返回 Too Many Requests(请求过多)错误。
API 返回:
- timestamp(时间戳)
- latitude(纬度)
- longitude(经度)
- altitude(高度)
- velocity(速度)
与此同时,模型中包含一个 expensive computation(耗时计算),用于演示 rs‑x 如何处理依赖关系。
核心思路: 只重新计算实际依赖于已更改数据的部分。
演示
该 API 每隔几秒使用 RxJS 轮询一次。
const $ = api.rxjs;
const rsx = api.rsx;
const issRaw$ = $.interval(2000).pipe(
$.startWith(0),
$.switchMap(() =>
$.from(
fetch('https://api.wheretheiss.at/v1/satellites/25544')
.then(r => r.json())
)
),
$.map(data => ({
ts: Number(data.timestamp ?? 0),
lat: Number(data.latitude ?? NaN),
lon: Number(data.longitude ?? NaN),
altKm: Number(data.altitude ?? NaN),
velKph: Number(data.velocity ?? NaN),
})),
$.shareReplay({ bufferSize: 1, refCount: true })
);
派生的响应式数据
接下来我们派生一个流,仅在国际空间站(ISS)位置有意义变化时发出。
const geoInputs$ = issRaw$.pipe(
$.map(x => ({
latQ: Math.round(x.lat * 100) / 100,
lonQ: Math.round(x.lon * 100) / 100,
})),
$.distinctUntilChanged(
(a, b) => a.latQ === b.latQ && a.lonQ === b.lonQ
)
);
这可以防止不必要的重新计算。
响应式模型
const model = {
iss: issRaw$,
geo: geoInputs$,
heartbeat: 0,
expensiveRuns: 0,
// Expensive computation
expensiveGeoScore(lat, lon) {
model.expensiveRuns++;
let acc = 0;
for (let i = 0; i {
model.heartbeat++;
}, 5000);
- 进入全屏模式
- 退出全屏模式
这 不会触发昂贵的计算。
为什么?因为该表达式仅依赖于:
geo.latQgeo.lonQ
rs‑x 会自动跟踪这些依赖。
为什么这很重要
在许多系统中,你需要手动管理重新计算:
useMemouseEffect- 依赖数组
- 缓存
- 手动优化
使用 rs‑x,规则很简单:描述你的数据及其关系。
运行时会构建一个 reactive dependency graph,并仅重新计算所需的部分。
心智模型
把它想象成电子表格。如果单元格 A1 发生变化:
- 只有依赖 A1 的单元格会更新
- 不相关的单元格保持不变
rs‑x 将此模型引入 JavaScript 表达式。
结果
演示创建了一个 响应式计算图,它:
- 处理异步流
- 自动跟踪依赖
- 避免不必要的重新计算
- 使代码保持声明式且简洁
没有异步编排。
没有手动依赖跟踪。
只需描述数据。
支持项目
如果您喜欢 rs‑x,考虑支持该项目:
GitHub Sponsors – robert‑sanders‑software‑ontwikkeling
这有助于我继续为开发者构建 开源工具。
标签
javascript #reactiveprogramming #rxjs #opensource #webdevelopment
