DEV Community

Cover image for Ng-News 25/15: Native Observables
ng-news for This is Angular

Posted on

8 1 1 2 1

Ng-News 25/15: Native Observables

Native Observables are now available in Chrome 135, offering deep integration into the Web API with .when() methods and default multicasting behavior. Careful: They differ a little bit from RxJS Observables in structure and behavior (e.g., Promise-returning methods, AbortController for cancellation).

Native Observables

Native Observables have landed in Chrome 135 — but don’t confuse them with the RxJS Observables we’re used to. Before diving into the differences, let’s first look at how they’re integrated into the existing Web APIs.

Traditionally, we use addEventListener() to register callbacks on DOM elements. With native Observables, these same elements now expose a when() method that returns an Observable — a sign of how deeply integrated this feature is.

// until now
document.addEventListener('mousemove', console.log);

// with native Observables
document.when('mousemove').subscribe(console.log);
Enter fullscreen mode Exit fullscreen mode

Key Differences from RxJS Observables

1. Multicasting Behavior by Default

Native Observables are shared by default. That means they’re multicasting, only activate on the first subscriber, and do not replay past values. This is similar to RxJS's share() operator — but importantly, not shareReplay().

Given the following code in RxJS:

const numbers$ = new Observable((subscriber) => {
  subscriber.next(1);
  subscriber.next(2);
  setTimeout(() => {
    subscriber.next(3);
  });
});
numbers$.subscribe((n) => console.log(`Sub 1: ${n}`));
numbers$.subscribe((n) => console.log(`Sub 2: ${n}`));
Enter fullscreen mode Exit fullscreen mode

We would get the following output:

Sub 1: 1
Sub 1: 2
Sub 2: 1
Sub 2: 2
Sub 1: 3
Sub 2: 3
Enter fullscreen mode Exit fullscreen mode

With native Observables, though, the same code would produce the following output:

Sub 1: 1
Sub 1: 2
Sub 1: 3
Sub 2: 3
Enter fullscreen mode Exit fullscreen mode

That is because the first subscription triggers the execution within the Observable and numbers 1 and 2 are emitted synchronously. Only value 3 emits in an asynchronous task and is therefore also consumed by the second subscriber.

2. Operators as Methods

Instead of using pipe() like in modern RxJS, native Observables go back to the roots: operators like map() or filter() are methods directly on the Observable instance.

This would be the RxJS-valid code:

// RxJS Observables

const numbers$ = new Observable<number>((subscriber) => {
  subscriber.next(2);
  subscriber.next(4);
  subscriber.next(8);
});

numbers$
  .pipe(
    map((n) => n * 2),
    tap((n) => console.log(`tap: ${n}`)),
    filter((n) => n < 10),
  )
  .subscribe(console.log);
Enter fullscreen mode Exit fullscreen mode

The same code but with native Observables:

// Native Observables

const numbers$ = new Observable((subscriber) => {
  subscriber.next(2);
  subscriber.next(4);
  subscriber.next(8);
});

numbers$
  .map((n) => n * 2)
  .inspect((n) => console.log(`tap: ${n}`)) // <-- that's tap()
  .filter((n) => n < 10)
  .subscribe(console.log);
Enter fullscreen mode Exit fullscreen mode

3. Promise-returning Methods

Some operators (like first(), last(), reduce(), and forEach()) return Promises, not Observables. They would also replace the necessity for an explicit subscribe.

This makes them easier to use in async/await flows.

In RxJS, there are utility functions to map an Observable into a Promise:

// RxJS

const countdown = new Observable((subscriber) => {
  let counter = 1;
  const intervalId = setInterval(() => {
    subscriber.next(counter++);
    if (counter > 5) {
      clearInterval(intervalId);
      subscriber.complete();
    }
  });
});

await lastValueFrom(countdown.pipe(tap(console.log)));
console.log('ended');
Enter fullscreen mode Exit fullscreen mode

With native Observables, last() returns already the Promise instead an Observable.

// Native Observables

const countdown = new Observable((subscriber) => {
  let counter = 1;
  const intervalId = setInterval(() => {
    subscriber.next(counter++);
    if (counter > 5) {
      clearInterval(intervalId);
      subscriber.complete();
    }
  });
});

await countdown.inspect(console.log).last();
console.log("ended");
Enter fullscreen mode Exit fullscreen mode

4. AbortController instead unsubscribe()

Unsubscribing is handled via AbortController, similar to how we cancel fetch() requests. Angular’s new resource() function uses this same pattern as well, so this approach should already feel familiar.

In RxJS, we usually use unsubscribe, when the subscription does not want to receive values anymore.

// RxJS

const numbers$ = new Observable<number>((subscriber) => {
  subscriber.next(1);
  subscriber.next(2);
});

const subscription = numbers$.subscribe((value) => {
  console.log(value);
  if (value >= 1) {
    console.log('aborting/unsubscribing (even synchronously)');
  }
});

subscription.unsubscribe();
Enter fullscreen mode Exit fullscreen mode

Since native Observables are much more integrated into the Web API, the unsubscription is done with the AbortController:

// Native Observables

const numbers$ = new Observable((subscriber) => {
  subscriber.next(1);
  subscriber.next(2);
});

const abortController = new AbortController();

numbers$.subscribe(
  (value) => {
    console.log(value);
    if (value >= 1) {
      console.log("aborting/unsubscribing (even synchronously)");
      abortController.abort(); // <-- "unsubscribe" here
    }
  },
  { signal: abortController.signal },
);

Enter fullscreen mode Exit fullscreen mode

5. Teardown via addTeardown()
The constructor function of an Observable no longer returns a teardown function. Instead, teardown logic is registered using addTeardown().

RxJS Version:

// RxJS Observable

const numbers$ = new Observable<number>((subscriber) => {
  subscriber.next(1);
  subscriber.next(2);

  return () => console.log('complete inside the observable');
});
Enter fullscreen mode Exit fullscreen mode

With native Observables, the teardown function will be more obvious:

// Native Observables

const numbers$ = new Observable((subscriber) => {
  subscriber.addTeardown(() => {
    console.log("completes inside the observable");
    subscriber.complete();
  });

  subscriber.next(1);
  subscriber.next(2);
});
Enter fullscreen mode Exit fullscreen mode

Will Observables Become a Language-Level Feature?

It’s still uncertain whether Observable will become a language-level standard (like Promise) — or if it will remain a browser-level Web API. That distinction will shape how deeply integrated they become across platforms.

https://github.com/wicg/observable?tab=readme-ov-file#standards-venue


What About RxJS?

RxJS 8 development had paused while waiting for native Observable readiness. Now that they’ve landed, RxJS will move forward with integrating them — including shims for environments where they aren’t available yet.

https://github.com/ReactiveX/rxjs/issues/6367


And Angular Signals?

No change there. Signals are for state, while Observables are for events — like DOM events or async completions. Observables can still be useful to model these event triggers, but state management in Angular is moving to Signals, not back to BehaviorSubject.

Relationship between Signals and Observables


To experiment with the examples, fine a ready to use starter at https://stackblitz.com/edit/stackblitz-starters-ybgwzhbc

Framework Waves

At the dotJS conference, Sarah Drasner gave a talk on how modern frontend frameworks have been continuously influencing one another.

She pointed out, for instance, that Qwik’s approach to resumability was likely inspired by Wiz — Google’s internal framework that is now being integrated into Angular.

Naturally, she focused on Angular’s current role in this evolving landscape. With features like incremental hydration, Angular is now in a position to influence other frameworks in return.

She concluded her talk by mentioning a new open-source library called tsurge, which she suggested could complement Angular’s ng update capabilities.

NgRx 19.1

NgRx 19.1 was released. It comes with a new features for the SignalStore in the areas of

  • Testing
  • Custom Features
  • withEntities

https://github.com/ngrx/platform/blob/main/CHANGELOG.md#1910-2025-04-01

Tiger Data image

🐯 🚀 Timescale is now TigerData: Building the Modern PostgreSQL for the Analytical and Agentic Era

We’ve quietly evolved from a time-series database into the modern PostgreSQL for today’s and tomorrow’s computing, built for performance, scale, and the agentic future.

So we’re changing our name: from Timescale to TigerData. Not to change who we are, but to reflect who we’ve become. TigerData is bold, fast, and built to power the next era of software.

Read more

Top comments (4)

Collapse
 
yaireo profile image
Yair Even Or

Why do you keep mentioning RxJS? I don't know RxJS and I don't care.

Collapse
 
ng_news profile image
ng-news

What do you want to say? This is an article on Angular and you know that RxJS is heavily used in Angular.

Collapse
 
yaireo profile image
Yair Even Or

I don't know Angular at all, so I also do not now RxJS at all, what is it and where is it used.

I clicked to read this article because header shows "native observables" with Chrome logo, so I wanted to learn what does this mean. not to get a whole lecture about angular and RxJS.

What I am saying this article header banner mislead me. I will filter out everything angular-related from now on

Thread Thread
 
ng_news profile image
ng-news

I see. It is little bit more complicated.

So RxJS used to be a library when it came to Reactive Programming in JavaScript. Rx means Reactive.
In RxJS, the basic type is the Observable and - generally speaking - it allows you to manage events. Angular has been using RxJS for more than 10 years now. So, when you are curious how RxJS can be used in an application, Angular might be good to check out some use cases.

Meanwhile, that Observable type from RxJS should be come standardized and Chrome did the first step integrating it natively.

This article is about the differences between RxJS and Observables. You don't need to know or use Angular in order to run the examples.

But since Ng-News is Angular News (ng is typically the synoym to Angular), there is also some Angular-related stuff at the end.

Scale globally with MongoDB Atlas. Try free.

Scale globally with MongoDB Atlas. Try free.

MongoDB Atlas is the global, multi-cloud database for modern apps trusted by developers and enterprises to build, scale, and run cutting-edge applications, with automated scaling, built-in security, and 125+ cloud regions.

Learn More

👋 Kindness is contagious

Delve into this thought-provoking piece, celebrated by the DEV Community. Coders from every walk are invited to share their insights and strengthen our collective intelligence.

A heartfelt “thank you” can transform someone’s day—leave yours in the comments!

On DEV, knowledge sharing paves our journey and forges strong connections. Found this helpful? A simple thanks to the author means so much.

Get Started