Skip to content

skip

The skip() operator is quite straightforward: it tells an Observable stream to simply ignore the first N values it emits. After skipping the specified number of items, it will then allow all subsequent emissions to pass through normally.

Think of it as telling someone to start counting after a certain number. If you say skip(3), you're essentially saying "Ignore the 1st, 2nd, and 3rd things that happen, but tell me about the 4th, 5th, 6th, and so on."

Key Characteristics#

  1. Counts and Ignores: It keeps an internal count of how many items have been emitted by the source.
  2. Skips the Start: It prevents the first N emissions from reaching the subscriber or subsequent operators in the pipe.
  3. Emits After Skipping: Once N items have been skipped, all following items are emitted without further modification by skip().
  4. Argument: It takes one argument: count (the number of emissions to skip).

Real-World Analogy#

Imagine you are subscribing to a news feed that sends updates every hour. However, you know that the first 2 updates of the day are always just routine system checks or old news summaries that you don't care about.

You can use skip(2) on this news feed stream. This way, you won't be bothered by the first two updates each day. You'll only start receiving notifications from the 3rd update onwards, which contains the actual news you're interested in.

Angular Example: Ignoring Initial Value from `BehaviorSubject#

A common use case in Angular involves BehaviorSubject or ReplaySubject(1). These types of Subjects store the "current" value and emit it immediately to any new subscriber. Sometimes, you only want to react to future changes pushed to the Subject, not the value it happens to hold at the exact moment you subscribe.

Let's say you have a service managing user authentication status:

import { Injectable } from "@angular/core";
import { BehaviorSubject } from "rxjs";

@Injectable({ providedIn: "root" })
export class AuthService {
  // Initially, user is logged out. Emits `false` immediately to new subscribers.
  private loggedInStatus = new BehaviorSubject<boolean>(false);
  isLoggedIn$ = this.loggedInStatus.asObservable();

  login() {
    // Simulate successful login
    console.log("AuthService: User logged in.");
    this.loggedInStatus.next(true);
  }

  logout() {
    console.log("AuthService: User logged out.");
    this.loggedInStatus.next(false);
  }
}

// --- In a Component ---
import { Component, DestroyRef, inject, signal } from "@angular/core";
import { NgIf } from "@angular/common"; // Import NgIf for the template
import { takeUntilDestroyed, toSignal } from "@angular/core/rxjs-interop"; // Core interop functions
import { filter, skip } from "rxjs/operators";
import { AuthService } from "./auth.service"; // Assuming AuthService exists as defined previously

@Component({
  selector: "app-login-watcher",
  standalone: true,
  imports: [NgIf],
  template: `
    <div>
      User is currently: {{ isLoggedIn() ? "Logged In" : "Logged Out" }}
    </div>

    <div *ngIf="loginMessage()" style="color: green;">{{ loginMessage() }}</div>

    <button (click)="authService.login()">Log In</button>
    <button (click)="authService.logout()">Log Out</button>
  `,
})
export class LoginWatcherComponent {
  protected authService = inject(AuthService);
  private destroyRef = inject(DestroyRef);

  isLoggedIn = toSignal(this.authService.isLoggedIn$, { initialValue: false });

  // Use a signal to hold the dynamic login message
  loginMessage = signal<string>(""); // Initialize with an empty string

  constructor() {
    this.setupLoginSubscription();
  }

  private setupLoginSubscription(): void {
    this.authService.isLoggedIn$
      .pipe(
        skip(1),
        // Optional: Filter for only 'true' values if you only care about login events
        filter((isLoggedIn) => isLoggedIn === true),
        Automatically unsubscribe when the component is destroyed
        takeUntilDestroyed(this.destroyRef)
      )
      .subscribe(() => {
        console.log("LoginWatcherComponent: Detected LOGIN event!");
        // Use the signal's .set() method to update the state
        this.loginMessage.set(
          `Welcome back! Login detected at ${new Date().toLocaleTimeString()}`
        );

        // Clear the message after a few seconds using standard setTimeout
        setTimeout(() => {
          // Use .set() again to clear the signal's value
          this.loginMessage.set("");
        }, 5000);
      });

    console.log(
      "LoginWatcherComponent initialized. Waiting for login events..."
    );
  }
}

Explanation of the Angular Example:

  1. AuthService uses a BehaviorSubject (loggedInStatus) initialized to false.
  2. isLoggedIn is created using toSignal, which converts the isLoggedIn$ Observable into a signal. This is useful for displaying the current state reactively in the template (isLoggedIn()). toSignal requires an initialValue.
  3. The skip(1) operator intercepts the initial false emission and discards it.
  4. The subscribe block does not run initially.
  5. Later, if the user clicks the "Log In" button, authService.login() calls this.loggedInStatus.next(true).
  6. This new value true is emitted by the BehaviorSubject.
  7. skip(1) has already done its job (skipped one item), so it lets true pass through.
  8. The optional filter(isLoggedIn => isLoggedIn === true) also lets true pass.
  9. The subscribe block now executes, logging the message and updating the component's loginMessage property, because a new value was emitted after the initial skipped one.

Summary#

skip(N) is useful when you need to disregard a known number of initial emissions from an Observable stream, allowing you to focus on the values that come after that initial phase.