C.W.K.
Stream
Lesson 01 of 05 · published

A Command Sent Is Not a State Changed

~12 min · observed-state, async, optimistic-ui, state

Level 0Reel Novice
0 XP0/39 lessons0/12 achievements
0/100 XP to next level100 XP to go0% complete
"A submitted command is not a visible state change until the engine acknowledges it."

The Little Lie That Feels Like Speed

The fastest-feeling way to build a pause button is to flip your UI the instant the user clicks: set isPaused = true, then tell the engine to pause. It feels instant because it is instant — the UI didn't wait for anything. It also just told a small lie. The engine hasn't paused yet. Usually it will, a few milliseconds later, and nobody notices. Sometimes it won't — a slow frame, an error, a race — and now your button says one thing while the video does another.

Multiply that across pause, seek, track selection, speed, and full screen, and you've built a player whose controls are a story about what you asked for, not a report of what happened. That gap is where the maddening bugs live.

Command Is a Request, Reality Is a Report

The fix is a mindset before it's code: a command you send is a request, and the engine's observed state is the only report. You already built the reporting channel in Track 2 — observed properties. Now you use it for real: send the request, and let the observed property update the UI when the engine's value actually changes. The UI stops predicting and starts reflecting.

A command is a request, never a result. The only thing that makes a state true is the system reporting it back. Update your UI from what came back, not from what you sent — the two are different events, and treating them as one is the root of every 'the button is wrong' bug.

The same feature, told as a lie and as the truth:

Optimistic UI has a place — this isn't it. For cheap, reversible, near-certain actions (liking a post, toggling a local setting), predicting success and correcting on failure is good UX. For playback truth — is it paused, where is the picture — a wrong prediction is a lie the user watches happen. Reserve optimism for what's cheap to be wrong about.

Pending Is an Honest State

Between request and report there's a real, nameable moment: pending. Ashen Reel is comfortable showing it — a brief hint that the action is in flight — because pending is honest and a false 'done' is not. Naming the in-between state is what lets the UI tell the truth continuously: requested, then pending, then whatever the engine actually reports. No step pretends to be a later step.

Code

The same pause button, as a lie and as the truth·swift
// BAD: optimistic. We declare the outcome before the engine acts.
func pauseNaive() {
    isPaused = true                              // UI now CLAIMS paused...
    try? mpvCommand(mpv, ["set", "pause", "yes"]) // ...but did it? we never checked.
}

// GOOD: send a request; let the observed 'pause' property report the truth.
func pause() {
    try? mpvCommand(mpv, ["set", "pause", "yes"])  // a REQUEST, nothing more
    // isPaused is updated ONLY by the observed-property handler (lesson 2.4),
    // when the ENGINE's pause flag actually flips. Until then the UI may show
    // a 'pending' hint -- never a false 'paused'.
}

// The difference isn't style. In the BAD version, isPaused and the engine
// can disagree forever. In the GOOD version, they cannot -- there's one source.

External links

Exercise

Find a place in your UI (or API client) where you update state immediately after sending a command — a toggle, a status label, a progress indicator. Ask: what happens if the command is slow, fails, or is overridden by the system? Is the action cheap-and-reversible (optimism is fine) or a truth the user relies on (reflect the real result)? Rewrite one case so the UI updates from the confirmed result, with an honest 'pending' in between.
Hint
The question for every optimistic update: 'how bad is it if my prediction is wrong, and would the user notice?' Cheap and unnoticed → optimism is fine. Expensive or user-visible truth → send the request, show pending, and update only when reality reports back.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.