Question presented to candidate:
"In a route guard we read the current user with this.auth.user$.pipe(first()), and in a test it blew up with EmptyError: no elements in sequence. A colleague says take(1) would have been fine. What is the difference between take(1) and first(), and which one would you use where?"
What a strong answer should cover:
- Both take the first value, complete, and unsubscribe from the source immediately.
- The difference is the empty case: if the source completes without emitting,
take(1)just completes, whilefirst()errors withEmptyError("no elements in sequence"). first()also accepts a predicate and a default value:first(pred)isfilter(pred)+take(1)+ an EmptyError if nothing matched;first(pred, defaultValue)emits the default instead of erroring.- Choose by intent:
first()when "no value" is a bug you want to surface;take(1)when "no value" is acceptable. - Strong candidates know the guard twist: the Angular Router itself reports a guard Observable that completes empty as a
NavigationErrorwithEmptyError, sotake(1)does not avoid the error there; the fix is to make the source emit. - Mention
firstValueFrom()(rejects with EmptyError, accepts{ defaultValue }) for async/await code, and that neither operator helps if the source never emits or completes (the stream just hangs).
Clarifying questions expected:
- "Can this source legitimately complete without a value (an empty cache, a filtered selector)?"
- "Is this used in a guard, resolver or effect where an error would block navigation or kill the stream?"
Code / implementation expected: Yes β both operators on an empty and a non-empty source, showing the EmptyError.