17 Jul 2023
4 min

Why Angular signals won’t replace RxJs

After the Angular team shared news about introducing signals in Angular 16, it was all the entire community could talk about. 

With everyone having that level of interest and fascination with this new feature, you might wonder if Angular is moving towards completely removing RxJs from the codebase. However, that is not the case. 

In this article, I will explain why RxJs won’t be replaced by signals and remain a library available to developers. 

The impact of signals

Angular signals are a new reactive primitive introduced to Angular in version 16. Before, we were able to use signals occasionally. For example, in Solid.js. So this concept is well known by Angular developers. You can find more information about signals in this article.

Signals will change our approach to writing code, change detection, and improve application performance. Considering the nature of signals and their synchronous data flow, they have some limitations which are perfectly filled by RxJs.

Signals vs RxJs

RxJs can be asynchronous. What does that mean? RxJs can do many operations in parallel, independently. The perfect example of a situation like that was presented at an wp.angular.love meetup by Maciej Wójcik 

In this case, it is possible that the `data` variable doesn’t have any value when it is assigned to the `someValue` variable. That is because data is still being downloaded, and the subscription doesn’t run yet.

On the other hand, signals are synchronous. That means that they run operations in a specific order. That works great when we use signals to store data or in the template.

Why RxJs is staying

Angular contains an API to convert signals to observable and observable to signal. This looks like a clear statement that we should use both approaches and use their different benefits. 

Here is an example of what  the above-mentioned conversion looks like:

  step = signal('create');
  step$ = toObservable(this.step);
  ngOnInit() {
    this.step$.subscribe((step) => {
      console.log(step);
    });
  }


  users$ = of([{ id: 1, name: 'John Smith' }]);
  users = toSignal(this.users$);

Most of us know the cases where signals are better than RxJs. But when is RxJs better than signals? Let’s take a look.

First, RxJs is better equipped to handle events from different sources, which need to be mapped and filtered. Signals can also handle those events, but they become less readable and more complicated than with RxJs. A significant number of RxJs operators gives us a wide range of possibilities.

A good and classic example where using RxJs is simpler than signals is listening for input change with debounceTime operator. Code written only using RxJs looks like that: 

 this.control.valueChanges.pipe(
      debounceTime(300),
    ).subscribe(value => {
      this.search.emit(value)
    })

Whereas code using  only signals can look like that:

@Component({
selector: 'app-root',
template: `
<input [ngModel]="searchValue()" (ngModelChange)="searchValue.set($event)">
`,
styleUrls: ['./app.component.scss']
})
export class AppComponent {
searchValue = signal('');
debouncedSearchValue = signal(this.searchValue());


constructor() {
let timeoutId: ReturnType<typeof setTimeout> | undefined;
effect((onCleanup) => {
const search = this.searchValue();


timeoutId = setTimeout(() => {
this.debouncedSearchValue.set(search);
}, 500);


onCleanup(() => {
clearTimeout(timeoutId);
});
});


effect(() => {
console.log(this.debouncedSearchValue());
});
}
}

As we can see, code that uses RxJs is simpler and more readable for a developer. But we should remember that we are able to write this code with both approaches. Using signals and RxJs together gives us more benefits than using only one approach.

Generally speaking, asynchronous events handling is really challenging to achieve when only using signals. Additionally, RxJs have existed for many years, are used in a significant number of projects, and with this are reliable and well-tested. 

Michael Hladky spoke on Twitter about re-creating and adjusting all RxJs operators to be compatible with signals. He said this is not the most efficient approach.

Network requests are another case when RxJs come in handy. XHR requests are asynchronous. Observable allows us to handle, map, and handle events like success, error, and completion. 

With RxJs operators, we can re-run requests or cancel them, which is impossible with signals. Signals don’t have classic and well-known operators like catchError or switchMap, which are responsible for canceling requests at a specific moment the new one comes with new data. 

Additionally, one of the most significant Angular features is backward compatibility. Completely removing RxJs from the framework will force Angular developers to spend an enormous amount of time on refactoring, something quite challenging to carry out.  In the case of many projects, a change like that will mean rewriting the entire project. 

Summary

Signals are, without a doubt, a revolution when it comes to performance and the approach to creating applications in Angular. Still, RxJs is a powerful tool that helps us handle asynchronous events easily and comfortably. 

From what I’ve seen, I am convinced that signals will never fully replace RxJs. Signals are able to replace only the synchronous parts of RxJs. But it’s best to combine these two approaches when building Angular applications. That will be more beneficial to app performance and the developer experience. 

Frequently asked questions

Will Angular signals replace RxJS?

No, Angular signals are not expected to replace RxJS, because signals are synchronous while RxJS handles asynchronous events, cancellation, and retries. The article argues signals can only replace the synchronous parts of RxJS, so the best approach is combining both. Angular also provides toObservable() and toSignal() to convert between the two.

How do I convert an Observable to a signal in Angular?

Use the toSignal() function to convert an Observable into a signal. For example, users = toSignal(this.users$) turns the users$ observable into a signal you can read in templates. Conversely, toObservable() turns a signal into an Observable that you can subscribe to, which lets you combine both reactive approaches in one component.

When should I use RxJS instead of signals in Angular?

Use RxJS when you handle asynchronous events from multiple sources that need mapping and filtering, or when you need operators. The article notes that signals work for storing data and driving templates, but RxJS operators like debounceTime, switchMap, and catchError are simpler for event streams, input debouncing, and cancelling or re-running network requests.

How do I debounce input changes in Angular, RxJS or signals?

Using RxJS, pipe valueChanges through the debounceTime(300) operator and subscribe to emit the value, which is short and readable. Doing the same with signals requires effect() calls, a setTimeout and onCleanup callback to clear timers, which becomes more complicated. Signals can do it, but RxJS is the simpler choice for debouncing.

Can signals cancel HTTP requests like RxJS does in Angular?

Not in the way RxJS does, according to the article. RxJS operators such as switchMap and catchError let you cancel a request when a new value arrives, re-run requests, or handle success, error, and completion events. Signals don't have equivalent operators, so network requests are a case where RxJS remains the better fit.

Share this post

Sign up for our newsletter

Stay up-to-date with the trends and be a part of a thriving community.