DEV Community

Robert Sanders
Robert Sanders

Posted on

Reactive Data Without the Async Headaches

Modern frontend development often turns into a battle with async code:

  • async/await
  • subscriptions
  • dependency arrays
  • memoization
  • race conditions
  • unnecessary recomputations

What if you could simply describe your data and let the system
handle the rest?

That's exactly the idea behind rs-x.

To demonstrate it, I built a small demo that tracks the real-time
position of the International Space Station (ISS)
.

👉 Live demo: https://robert-sanders-software-ontwikkeling.github.io/rs-x/demo


The Idea

The demo polls a public ISS API and exposes the data as part of a
reactive model. ⚠️ Note > If many people open the demo simultaneously, the public ISS API may return Too Many Requests errors due to rate limits.

It returns:

  • timestamp
  • latitude
  • longitude
  • altitude
  • velocity

At the same time the model contains an expensive computation to
demonstrate how rs-x handles dependencies.

The key idea:

Only recompute what actually depends on changed data.


The Demo

The API is polled every few seconds using 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 })
);
Enter fullscreen mode Exit fullscreen mode

Derived Reactive Data

Next we derive a stream that only emits when the ISS position
meaningfully changes.

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
  )
);
Enter fullscreen mode Exit fullscreen mode

This prevents unnecessary recomputation.


The Reactive Model

Now we define the model.

const model = {
  iss: issRaw$,
  geo: geoInputs$,

  heartbeat: 0,
  expensiveRuns: 0,

  expensiveGeoScore(lat, lon) {
    model.expensiveRuns++;

    let acc = 0;
    for (let i = 0; i < 3_000_000; i++) {
      acc += Math.sin(i);
    }

    const score =
      Math.round((Math.abs(lat) * 1.7 + Math.abs(lon) * 0.9) * 100) / 100;

    return score;
  },
};
Enter fullscreen mode Exit fullscreen mode

The function intentionally simulates a CPU-heavy computation.


A Completely Declarative Expression

Finally we describe the data we want.

return rsx(`
({
  heartbeat,

  issTs: iss.ts,
  altKm: iss.altKm,
  velKph: iss.velKph,

  lat: iss.lat,
  lon: iss.lon,

  geoScore: expensiveGeoScore(geo.latQ, geo.lonQ),

  expensiveRuns
})
`)(model);
Enter fullscreen mode Exit fullscreen mode

That's it.


What Happens When Data Changes?

Two independent updates happen in the demo.

ISS position updates

When the ISS position changes:

  • lat
  • lon

The expensive computation runs again.

Heartbeat updates

A separate part of the model changes every few seconds.

setInterval(() => {
  model.heartbeat++;
}, 5000);
Enter fullscreen mode Exit fullscreen mode

This does not trigger the expensive computation.

Why?

Because the expression only depends on:

  • geo.latQ
  • geo.lonQ

rs-x tracks these dependencies automatically.


Why This Matters

In many systems you need to manually manage recomputation:

  • useMemo
  • useEffect
  • dependency arrays
  • caching
  • manual optimization

With rs-x the rule is simple:

Describe your data and its relationships.

The runtime builds a reactive dependency graph and recomputes only
what is required.


The Mental Model

Think of it like a spreadsheet.

If cell A1 changes:

  • only cells that depend on A1 update
  • unrelated cells remain untouched

rs-x brings this model to JavaScript expressions.


The Result

The demo creates a reactive computation graph that:

  • handles async streams
  • tracks dependencies automatically
  • avoids unnecessary recomputation
  • keeps code declarative and simple

No async orchestration.

No manual dependency tracking.

Just describe the data.


Support the Project

If you like rs-x, consider supporting the project:

https://github.com/sponsors/robert-sanders-software-ontwikkeling

It helps me continue building open source tools for developers.


Tags

javascript #reactiveprogramming #rxjs #opensource #webdevelopment

Top comments (0)