In RxJS, given of(1, 2, 3, 4, 5, 1), what do take(3), takeWhile, skip(2) and skipWhile each emit with the predicate x < 3?
answer
- count versus predicate
- which ones complete early
- inclusive flag on takeWhile
- skipWhile never re-checks
basics
~20 stake(3) emits 1, 2, 3 and completes; takeWhile(x => x < 3) emits 1, 2 and completes at the 3; skip(2) and skipWhile(x => x < 3) both emit 3, 4, 5, 1, since skipWhile stops checking once its predicate fails.
solid answer
~40 s`take(3)` passes the first three values, `1, 2, 3`, then completes and unsubscribes from the source. `takeWhile(x => x < 3)` passes `1, 2` and completes on the first failing value, `3`, which it drops — pass `true` as the second argument (`inclusive`) to emit that value too, giving `1, 2, 3`. `skip(2)` drops the first two and passes `3, 4, 5, 1`; it never completes early. `skipWhile(x => x < 3)` drops until the predicate first returns `false`, then lets everything through, so the trailing `1` passes even though it matches. Contrast `filter(x => x < 3)`, which tests every value and emits `1, 2, 1`. The `take` family ends the stream; the `skip` family and `filter` only drop values.
code
ts · 10 linesimport { of, take, takeWhile, skip, skipWhile, filter } from 'rxjs';
const source$ = of(1, 2, 3, 4, 5, 1);
source$.pipe(take(3)).subscribe(console.log); // 1 2 3
source$.pipe(takeWhile(x => x < 3)).subscribe(console.log); // 1 2
source$.pipe(takeWhile(x => x < 3, true)).subscribe(console.log); // 1 2 3
source$.pipe(skip(2)).subscribe(console.log); // 3 4 5 1
source$.pipe(skipWhile(x => x < 3)).subscribe(console.log); // 3 4 5 1
source$.pipe(filter(x => x < 3)).subscribe(console.log); // 1 2 1go deeper
Recall the output of each operator on a simple sequence and which ones complete the stream early.
Explain the inclusive flag, the index argument to predicates, and why skipWhile stops checking after the first failure.
Pick the operator that bounds an endless feed, and spot a filter that should have been a takeWhile in a stream that never completes.
Relate operator choice to resource lifetime: which streams must end on their own and which rely on an outside signal to stop.
## Two families: ending versus dropping RxJS's filtering operators split by what they can do to the **lifetime** of a stream. - The **take family** — `take`, `takeWhile`, `takeUntil`, `first` — can **complete** the output before the source finishes, which also unsubscribes from the source. - The **skip family** — `skip`, `skipWhile` — and **`filter`** only decide which values pass. The output completes when the source completes, never earlier. That distinction matters for streams that never end on their own, such as `interval` or a live sensor feed: only the take family can stop them. ## The same source through each operator For `of(1, 2, 3, 4, 5, 1)`: | operator | emits | completes | |---|---|---| | `take(3)` | `1, 2, 3` | after the third value | | `takeWhile(x => x < 3)` | `1, 2` | on `3`, which is dropped | | `takeWhile(x => x < 3, true)` | `1, 2, 3` | on `3`, which is emitted | | `skip(2)` | `3, 4, 5, 1` | with the source | | `skipWhile(x => x < 3)` | `3, 4, 5, 1` | with the source | | `filter(x => x < 3)` | `1, 2, 1` | with the source | The interesting rows are the last value, `1`: - `takeWhile` never sees it — the stream already completed at `3`. - `skipWhile` lets it through — after the predicate first returns `false`, the operator **stops calling the predicate** for good. - `filter` tests it like every other value and emits it. ## Details from the RxJS 7.8 source 1. **`take(count)` with `count <= 0`** returns `EMPTY`: the output completes immediately and the source is never subscribed. 2. **`take` completes as soon as the count is reached**, not when the next value arrives, so `take(1)` on a slow source ends right after the first value. 3. **`takeWhile(predicate, inclusive = false)`** — `inclusive` defaults to `false`. With `true`, the value that failed the predicate is emitted before completion, handy when the terminating value is meaningful (the reading that crossed a threshold). 4. **Predicates receive an index**: `filter`, `takeWhile` and `skipWhile` call `predicate(value, index)`, so `filter((_, i) => i % 10 === 0)` keeps every tenth value. `skip(n)` is literally implemented as `filter((_, index) => n <= index)`. 5. **Type narrowing**: passing `Boolean` (for example `filter(Boolean)`) or a type guard narrows the output type; the `thisArg` overload of `filter` is deprecated in 7.8 in favour of a closure. ## Picking one in a sensor pipeline - Stop after a fixed number of samples: `take(n)`. - Record until a reading crosses a limit, including that reading: `takeWhile(r => r.celsius < 90, true)`. - Ignore the warm-up readings: `skip(n)` for a known count, `skipWhile(r => r.celsius === 0)` while the sensor reports zero. - Keep only valid readings for the whole life of the stream: `filter(r => r.ok)`. ## Why ending early matters Completing is not cosmetic. When `take` or `takeWhile` completes the output, RxJS **unsubscribes from the source**, which runs the source's teardown: a timer is cleared, a DOM listener is removed, a WebSocket subscription is released. `filter`, `skip` and `skipWhile` never trigger that teardown on their own — they keep the source running for as long as someone is subscribed. Completion also unlocks operators downstream that wait for it: - `toArray()` and `reduce()` emit their result only when the stream completes; - `last()` needs completion to know which value was last; - a `complete` callback in the subscriber runs, which is where a UI often hides a spinner. So `interval(1000).pipe(take(5), toArray())` emits one array after five seconds, whereas `interval(1000).pipe(filter(x => x < 5), toArray())` never emits at all. ## Interview traps A frequent mistake is using `filter` where `takeWhile` was meant and wondering why a stream never completes, or using `skipWhile` expecting it to re-check later values the way `filter` does.
- What does takeWhile's second argument change?It is `inclusive`, `false` by default. With `true`, the first value that fails the predicate is emitted before the stream completes, so `takeWhile(x => x < 3, true)` on `1, 2, 3, 4` emits `1, 2, 3`.
- What does take(0) do to the source subscription in RxJS 7.8?`take` with a count of zero or less returns `EMPTY`, so the output completes immediately and the source is never subscribed; no error is thrown.
saying these in an interview costs you the question
- skipWhile keeps testing every value, just like filter.
- takeWhile emits the value that fails its predicate by default.
- skip(n) completes the stream once n values have been skipped.
- filter completes the stream at the first value that fails the predicate.
- take(0) subscribes to the source and drops every value.