Take(1) vs First() vs Single() In RxJS/Angular

Take, First, and Single all appear to be almost identical in RxJS/Angular at first glance. But actually, they have some very important differences that could throw up some very weird errors if you aren’t expecting them. If you have spent any time working with C# LINQ expressions at all, you may actually find a bunch of this familiar.

In anycase, let’s jump right in.

Take(1)

Take(1) is the easiest of the three. It simply takes the first value output by the observable, then unsubscribes itself. As an example :

let myObservable = new Subject();

let subscription = myObservable.pipe(take(1)).subscribe(x => {
  //Do something with the data. 
  console.log(x);
});

myObservable.next("Foo");
myObservable.complete();

console.log(subscription.closed);

The output of this would be :

Foo
True

As we subscribed, output the value, then the subscription is closed. However consider the following :

let myObservable = new Subject();

let subscription = myObservable.pipe(take(1)).subscribe(x => {
  //Do something with the data. 
  console.log(x);
});

myObservable.complete();

console.log(subscription.closed);

Notice how we don’t call next on our observable at all. Well the output of this is still :

True

As we still abide by the observable being completed regardless of whether we got any value from it. Also consider the following :

let myObservable = new Subject();

let subscription = myObservable.pipe(take(1)).subscribe(x => {
  //Do something with the data. 
  console.log(x);
});

myObservable.next("Foo");
myObservable.next("Bar");
myObservable.complete();

console.log(subscription.closed);

This still outputs

Foo
True

Because we are using take(1). We are unsubscribing from the observable so any additional values are not pumped to the subscription.

First()

Now consider the following with First() :

let myObservable = new Subject();

let subscription = myObservable.pipe(first()).subscribe(x => {
  //Do something with the data. 
  console.log(x);
});

myObservable.next("Foo");
myObservable.complete();

console.log(subscription.closed);

This outputs the same as earlier.

Foo
True

But consider the following when we don’t call next on our observable :

let myObservable = new Subject();

let subscription = myObservable.pipe(first()).subscribe(x => {
  //Do something with the data. 
  console.log(x);
});

myObservable.complete();

console.log(subscription.closed);

We actually get :

Foo
ERROR : no elements in sequence - EmptyError

Why? Because we closed off our observable before even the first value was output. This may seem like an actual worse feature to have, but it’s an important distinction. If you are always expecting at least one value, then you may use first() to log errors when you don’t get any data. It’s almost signalling the intent of the code that there should always be atleast one value coming from the observable.

Finally, also consider the following code :

let myObservable = new Subject();

let subscription = myObservable.pipe(first(x => x == "Bar")).subscribe(x => {
  //Do something with the data. 
  console.log(x);
});

myObservable.next("Foo");
myObservable.next("Bar");
myObservable.complete();

console.log(subscription.closed);

Notice how we can pass a matching function to our first operator to tell us to only match on a certain value. This is an added piece of functionality that take(1) cannot do on it’s own!

Single()

Single is very similar to using First() in some ways, with an added restriction. Consider the following :

let myObservable = new Subject();

let subscription = myObservable.pipe(single()).subscribe(x => {
  //Do something with the data. 
  console.log(x);
});

myObservable.complete();

console.log(subscription.closed);

This also throws an error just like first() because we are completing the observable before any values are output. But also consider the following :

let myObservable = new Subject();

let subscription = myObservable.pipe(single()).subscribe(x => {
  //Do something with the data. 
  console.log(x);
});

myObservable.next("Foo");
myObservable.next("Bar");
myObservable.complete();

console.log(subscription.closed);

This results in a new error :

ERROR Sequence contains more than one element

While first() will simply take the first value and ignore all others, single() will take the first value but will throw an exception if there is more than 1 available. This is an important distinction because again, it signals the intent of the code that we are expecting 1, and only 1 value. If there is more than 1, then something as gone wrong and we should throw an exception.

Leave a Reply

Your email address will not be published. Required fields are marked *