See how these RxJS operators differ in emission, throwing errors and completion, when applied to different source Observables:
const { rxObserver } = require('api/v0.3');
const { timer, interval, merge } = require('rxjs');
const { first, single, take, ignoreElements } = require('rxjs/operators');
// A stream — one value at 5ms, complete at 10ms
const a$ = merge(timer(5), timer(10).pipe(ignoreElements()));
a$.subscribe(rxObserver('A stream — one value at 5ms, complete at 10ms'));
a$.pipe(first())
.subscribe(rxObserver('A :: first()'));
a$.pipe(take(1))
.subscribe(rxObserver('A :: take(1)'));
a$.pipe(single())
.subscribe(rxObserver('A :: single()'));
// B stream — two values at 5ms and 10ms, complete at 10ms
const b$ = interval(5).pipe(take(2));
b$.subscribe(rxObserver('B stream — two values at 5ms and 10ms, complete at 10ms'));
b$.pipe(first())
.subscribe(rxObserver('B :: first()'));
b$.pipe(take(1))
.subscribe(rxObserver('B :: take(1)'));
b$.pipe(single())
.subscribe(rxObserver('B :: single()'));
// C stream — no values, complete at 10ms
const c$ = timer(10).pipe(ignoreElements());
c$.subscribe(rxObserver('C stream — no values, complete at 10ms'));
c$.pipe(first())
.subscribe(rxObserver('C :: first()'));
c$.pipe(take(1))
.subscribe(rxObserver('C :: take(1)'));
c$.pipe(single())
.subscribe(rxObserver('C :: single()'));