skip to content

In RxJS, how do auditTime and sampleTime differ from throttleTime, and which one reports a scroll tracker's latest position?

level: middleimportance: should knowfreq 36%

answer

  1. whose clock starts the window
  2. latest instead of first
  3. value-started window versus fixed clock
  4. what happens on idle ticks

basics

~20 s

auditTime starts a window on a value and emits the latest value when it ends; sampleTime emits the latest value on a fixed clock; default throttleTime emits a window's first value. auditTime usually suits a scroll tracker's latest position.

solid answer

~40 s

All three thin a stream in windows; they differ in **which value** they emit and **what starts the window**. Default `throttleTime(ms)` emits the *first* value, then ignores the source for `ms`. `auditTime(ms)` starts its timer on the first value, ignores values meanwhile, and emits the *most recent* value when the timer ends, then waits for the next value to start again. `sampleTime(ms)` runs an independent clock from subscription and on each tick emits the latest value only if a new one arrived since the previous tick. For a scroll tracker, `auditTime(100)` delivers the latest position at most 100 ms late, including the resting position, and costs nothing while the page is idle.

code

ts · 8 lines
ts
import { fromEvent, map, auditTime } from 'rxjs';

const scrollY$ = fromEvent(window, 'scroll').pipe(
  auditTime(100),
  map(() => window.scrollY)
);

scrollY$.subscribe(y => console.log('position', y));

go deeper

for a junior

Recall that auditTime and sampleTime emit the latest value, while default throttleTime emits the first value of a window.

for a middle

Explain what starts each window, why sampleTime skips idle ticks, and why auditTime ends on the resting position.

for a senior

Choose between auditTime, sampleTime and a trailing throttle by cadence, idle cost and completion behaviour, and justify it for a scroll tracker.

for a principal

Relate the choice to rendering budgets: which update cadence the UI can afford and whether updates must align to activity or to a clock.

## Three operators, one family RxJS 7.8 has three operators that reduce a fast stream to at most one value per time window: - **`throttleTime(duration)`** — built on `throttle(() => timer(duration))`. With its default config it emits the **first** value, then ignores the source for `duration`. - **`auditTime(duration)`** — built on `audit(() => timer(duration))`. The first value **starts** a timer; values that arrive meanwhile overwrite a pending slot; when the timer ends, the **most recent** value is emitted. The next window starts only when another value arrives. - **`sampleTime(period)`** — built on `sample(interval(period))`. A clock **started at subscription** ticks every `period`; on each tick the latest value is emitted **if a new one arrived** since the previous tick. ## Side by side | | `throttleTime(100)` default | `auditTime(100)` | `sampleTime(100)` | |---|---|---|---| | what starts the window | a value | a value | subscription, then a fixed clock | | value emitted | first of the window | latest of the window | latest since the previous tick | | delay of the first emission | none | up to 100 ms | up to 100 ms, depending on clock phase | | resting value after activity stops | lost | emitted | emitted on the next tick | | timers while the source is idle | none | none | the clock keeps ticking | | source completes with an unsent value | dropped | emitted when the window ends, then completes | dropped; completes at once | ## Picking one for a scroll tracker The tracker wants the **current** position, not the position at the start of a window, and it must end on the **resting** position. 1. `throttleTime(100)` with the default config fails the second requirement. 2. `sampleTime(100)` meets both, but its clock runs for the life of the subscription, even while nobody scrolls, and its emissions are aligned to the clock rather than to the activity. 3. `auditTime(100)` meets both and does no work while the page is idle, which is why it is the usual answer. `throttleTime` with `{ leading: true, trailing: true }` is also correct at rest and adds an immediate first update; it is the right pick when the first reaction must not wait. ## A subtle difference: trailing throttle versus audit `throttleTime(100, asyncScheduler, { leading: false, trailing: true })` looks identical to `auditTime(100)`, and for a single burst it behaves the same. The difference is what happens **after** an emission: - the trailing throttle **opens a new window immediately** after it emits, so a value arriving 30 ms later is emitted at the end of that window, 100 ms after the previous emission; - `auditTime` goes **idle** after it emits, so a value arriving 30 ms later starts a fresh window and is emitted 130 ms after the previous emission. In practice the throttle keeps a steadier cadence during intermittent activity, and audit keeps a constant delay from the triggering value. ## Completion behaviour - `auditTime`: if a window is open with a pending value when the source completes, the operator waits for the window to end, emits the value, then completes — a behaviour introduced during the RxJS 7 betas. - `sampleTime`: completion of the source completes the output immediately; a value not yet sampled is dropped. - `throttleTime`: see its trailing option; with the default config, pending values are dropped. For finite sources, such as a recorded sequence of positions replayed in a test, these differences decide whether the last value appears. ## Duration-selector forms Each operator has a general form taking an observable instead of a number: `audit(value => …)`, `sample(notifier$)`, `throttle(value => …, config)`. Since RxJS 7 the duration or notifier must **emit a value** to trigger an emission — to release a held value or take a sample; a duration that only completes emits nothing. ## What interviewers probe - Which value each operator emits: first (default throttle) or latest (audit, sample). - Whether a clock runs while the source is idle (only `sampleTime`). - Whether the resting value arrives (audit and sample yes, default throttle no). - What happens at completion, because finite test streams expose it immediately.

  • Why can sampleTime cost more than auditTime on a page that is mostly idle?
    `sampleTime` subscribes to an `interval` when the output is subscribed and keeps it ticking for the whole subscription, whether or not the source emits. `auditTime` schedules a timer only when a value arrives, so an idle page schedules nothing.
  • What does sampleTime do with a value that arrives just before the source completes?
    It drops it. The source's completion is forwarded straight away and the pending value is never sampled. `auditTime` and `debounceTime`, by contrast, emit a pending value around completion.

saying these in an interview costs you the question

  • auditTime emits the first value of each window, like throttleTime.
  • sampleTime repeats the previous value on ticks with no new value.
  • sampleTime's clock starts when the first value arrives.
  • auditTime and trailing-only throttleTime are identical in every timing.
  • auditTime drops a pending value when the source completes.