skip to content

Subscription Lifecycle

Nothing happens until something subscribes and nothing stops until something cancels. Interviewers ask because an undisposed subscription outlives the request that created it.

on this pageshow

questions

5

A chat screen builds a message stream but no request is sent — what act starts the work, and what does it return?

level: juniorimportance: must knowfreq 72%

answer

  1. description, not work
  2. nothing runs until something asks
  3. subscribing wires and starts
  4. the call hands back a handle
  5. no handle, no cancel

basics

~20 s

Subscribing starts the work; building a stream only describes it. A subscription call attaches a subscriber to the source, triggers whatever the source does to produce values, and hands back a handle the caller uses to cancel that run.

solid answer

~40 s

Chaining stages together produces a *description* of work — an object that knows how to open the connection and transform each message, but has opened nothing. The description becomes a running sequence only when a subscriber attaches to it. That act wires the chain from subscriber up to source, starts whatever the source does to produce values, and returns a subscription handle representing this one run. The handle is what the caller keeps: it is how the screen later cancels and lets the source release what it holds. So a pipeline that is built and stored but never subscribed does nothing at all — no request, no error, no log line — and a pipeline that is subscribed while the handle is thrown away can never be stopped by its owner.

code

pseudocode · 11 lines
pseudocode
pipeline = messagesFrom(conversationId)
            .decrypt()
            .markAsRead()

// nothing has run yet: no connection, no decryption

subscription = pipeline.subscribe(
    onValue = function(message) { view.append(message) },
    onEnd   = function(reason)  { view.showEnded(reason) })

// the connection opens here, and `subscription` is the handle that stops it

go deeper

for a junior

Remember the one-line rule: nothing runs until something subscribes, and the subscribe call gives you a handle to keep. A screen with no data and no error usually never subscribed.

for a middle

Explain the mechanics: subscribing wires the chain, starts the source, and returns a per-run handle. Note that a fully in-memory source can finish before the subscribe call even returns.

for a senior

Show the operational consequence: silent screens come from never subscribing, and runs that outlive their owner come from discarding the handle. Make handle ownership explicit in review.

for a principal

Set the convention that every subscription has a named owner whose lifetime ends it, so that starting work and owning work are never separated across a codebase.

## A stream is a description until something subscribes A reactive source is a recipe for producing values, not the values themselves. Writing `messagesFor(conversation)` and chaining stages onto it builds an object that **knows how** to open a connection, decrypt each message and hand it on — but it has opened nothing and fetched nothing. The work begins at one moment: when a subscriber is attached to the end of the chain. That is why a screen that builds a pipeline and stores it in a field sits there silently: no request in the network log, no failure to explain it, nothing at all. Silence is the symptom of a missing subscription. Three things happen at that moment: 1. **The chain is wired.** Each stage attaches to the one above it, from the subscriber at the bottom up to the source at the top, so a path now exists in both directions — values will travel down it, and a cancel will travel up it. 2. **The source starts.** Whatever the source does to produce values — opening a connection, reading a file, arming a timer, registering with a live event feed — it does now, because a subscriber has asked for values. 3. **A handle comes back.** The subscribe call returns an object that stands for **this one run**: the caller's means of ending it early. ## What the handle is for The handle is the only part of the subscription the calling code holds directly, and it exists for one reason: a run that was started must be stoppable by whoever started it. - It **cancels**: asking the handle to cancel sends that request up the chain so the source can stop and release what it holds. - It **scopes ownership**: whoever holds the handle owns the run's lifetime, which is how a subscription gets tied to the screen that opened it. - It usually **reports whether the run is still live**, so cleanup code can avoid cancelling something that already ended. Discarding the handle is therefore not a stylistic sloppiness. It permanently removes the only lever the owner has, and the run continues until the source ends on its own — which, for a live conversation feed, is never. ## Built, subscribed, cancelled: what exists when | Moment | What exists | What is running | What the caller can do | |---|---|---|---| | Stages chained together | A description of the work | Nothing | Subscribe to it, or discard it harmlessly | | Subscribe called | A live subscription plus a handle | The source is producing | Receive values; cancel through the handle | | Cancel requested, or the source ends | A finished run | Nothing, once the source observes it | Subscribe again for a fresh run | The first row is what candidates miss. A described pipeline holds no connection, leaks nothing, and can be built in a constructor without consequence. It is inert. ## What subscribing does not promise Two honest refinements sit just past the basic answer, and interviewers like both. - **Subscribing does not promise that values arrive later.** A source backed by values already in memory may push every value and finish **before the subscribe call returns**. Code shaped as *subscribe, then store the handle, then decide what to do about early values* can be too late: the sequence is already over. Store state before subscribing, not after. - **Subscribing does not promise anything about where the work runs.** Whether the source's work happens on the caller's worker or elsewhere is a separate decision made by the pipeline, not by the act of subscribing. ## How to answer it in an interview Say the mechanism in one sentence — *nothing runs until something subscribes, and subscribing hands back the handle that stops it* — then give the two failure shapes it explains, because they are what the interviewer is really probing: - **Built but never subscribed:** a screen that shows no data and logs no error. The pipeline was assembled and nobody asked for values. - **Subscribed but the handle discarded:** a screen that works, and then keeps working after the user has closed it, because nothing can cancel it. Both shapes come from the same fact, stated from opposite ends: subscription is the boundary between describing work and owning a running piece of work.

  • The team stores the built pipeline in a field when the screen is constructed. Does that hold a connection open?
    No. A built pipeline is inert: it describes how to open the connection, transform values and finish, but holds none of those resources. Constructing and storing it is free, and discarding it needs no cleanup. Only a subscription owns running work, and only a subscription needs cancelling.
  • If the handle was discarded, is there any other way the run can end?
    Yes, but none of them is under the caller's control. The source may complete on its own, it may fail, or a stage inside the pipeline may cancel upstream as part of its own behaviour. For a live conversation feed none of those arrives, so a discarded handle means a run nothing can end.

Writing an order on a ticket costs the kitchen nothing; handing the ticket over is what starts the cooking, and the number you get back is how you call it off.

saying these in an interview costs you the question

  • Says the source starts producing as soon as the pipeline is built
  • Thinks a stored but unsubscribed pipeline holds an open connection
  • Treats the returned handle as the first value of the stream
  • Believes subscribing always returns before any value is delivered
  • Discards the handle and says nothing is lost by doing so
open as a page

A user closes a conversation screen and its message subscription is cancelled — what travels upstream, and when does the source stop?

level: middleimportance: must knowfreq 62%

basics

~20 s

A cancel request travels up the same chain the values came down: each stage stops asking its upstream, forwards the request and tears itself down. The source stops at the next point where it observes the request, not instantly.

open as a page

A message source opens a connection when subscribed — on which endings must its teardown run, and which ending is forgotten?

level: middleimportance: should knowfreq 50%

basics

~20 s

A subscription ends in one of three ways: normal completion, failure, or cancellation. Teardown must run on all three, and cancellation is the one teams forget, because it is the only ending the source did not choose.

open as a page

Closed chat screens keep receiving pushed messages and memory grows with every open-and-close cycle — how do you diagnose and fix it?

level: seniorimportance: should knowfreq 58%

basics

~20 s

Subscriptions are never cancelled, so each closed screen leaves a live one behind. The source holds the subscriber, the subscriber holds the screen, and nothing can be released. Fix it by tying every subscription's disposal to the lifetime of the owner that created it.

open as a page

A message subscription stops delivering values — how can the code tell a cancelled run from a completed one, and why does the difference matter?

level: seniorimportance: nice to knowfreq 30%

basics

~20 s

Completion arrives as a terminal signal from the source; cancellation is a request the subscriber sent upstream, after which it is simply not told anything. So a completion handler never fires on a cancel, while an any-ending hook fires on both and cannot distinguish them by itself.

open as a page