DEV Community

Blake Lamb
Blake Lamb

Posted on

RxJs: take(1) vs first()

Overview

When using Angular, it is inevitable that you will also commonly be using RxJs. Together these tools can do many amazing things. There is a seemingly infinite amount of different operators that I am constantly learning about. One that I have recently come across is first(). I always have used take(1) in order to access the first item emitted from an observable stream and then complete the observable. Finding out about first() initially seemed like a cool way to clarify my code a little more. But, after more research, I found out that there is enough of a difference that they may not necessarily be used interchangeably.

Bonus: chatGPT gave me an incorrect answer about the difference between the two operators. More on that later as well.

Setup - Create A Sandbox Project

Go to Stackblitz and start a new "RxJs" project. Or you can fork my Stackblitz here to allow you to follow along with my example.

Similarities Between take(1) & first()

take(1) is a great way to pick the first, and only the first, value from an observable stream. It is extremely simple to use as you can see here:

const source$: Observable<any> = from([1, 2, 3]);

source$
  .pipe(
    take(1),
    catchError((error) => {
      console.log({ error });
      return of();
    }),
    tap((value) => console.log({ value }))
  )
  .subscribe();
Enter fullscreen mode Exit fullscreen mode

The console.log will be value: 1, since that is the first item in the array.
(As a side note, if you don't know why it emits 1 instead of the whole array passed into the from(), check out my post about that operator here.)

Similarly, first() can perform the exact same functionality. In my opinion, it also accomplishes the task with more readability than take(1). An example of using first() would look like this:

const source$: Observable<any> = from([1, 2, 3]);

source$
  .pipe(
    first(),
    catchError((error) => {
      console.log({ error });
      return of();
    }),
    tap((value) => console.log({ value }))
  )
  .subscribe();
Enter fullscreen mode Exit fullscreen mode

You'll see in these two examples that they each perform the same task and log the same value, value: 1. Both operators also complete the observable after they have been called, so there is no need to unsubscribe from the observable.

Now we can look at where these operators behave differently and why you might choose one over the other.

Differences Between take(1) & first()

Lets change the source$ variable to now equal from([]). You'll now be able to find the difference between these operators.

If this were to happen while using take(1), you would never know as nothing would be logged, and no error would be caught.

On the contrary, if using first() and there is no value emitted from the observable, there will still be no value logged, but you would see a value for the error within the catchError block.

That is it! That is the difference between the two operators. Kind of anti-climactic right? Well lets find an example of why this difference could be important so you can know which operator to use in the future.

How Do I Choose One?

There will be many situations where you will never notice a difference between the two operators. But if you need to know for sure that there is a value being emitted, then the first() operator could come in handy.

For example, you might be building a login component where you're handling login credentials and, when the user clicks "Submit", pushing them to an observable. If the user clicks "Submit" and the username or password field does not contain values, then you could use the first() operator to throw an error and stop the login attempt.

Or you might be logging the user out of an application and it is not necessary to look for a specific value or any value at all. You know that the action will happen once and then the observable can complete. This would be a great situation for take(1).

Conclusion

There are so many solutions to the same problem, but not all solutions are created equally. So having more tools to use, allows you to be able to find the best solution to a given problem. take(1) and first() both have their place in the world of RxJs. Hopefully, now you are able to better find those places and use them appropriately.

Bonus: ChatGPT

I have been trying to use ChatGPT as a resource in my coding as well as my learning. It has been a great help to me in both aspects. I have found however that it is often wrong when explaining definitions to me. For this post, I asked it what the difference was between the two operators. Here is the answer that I got:

ChatGPT response to my question

Obviously, this is incorrect, and the real answer was the inverse of what it told me. All of this to say, even though ChatGPT is an awesome tool it is capable of being incorrect. So make sure to fact check with other sources before just taking its word as law.

Top comments (0)