Angular 2 — ObjectUnsubscribedError Woes

Filip Sufitchi
4 min readDec 21, 2016

--

If you have worked with Angular 2 any significant amount, you might have faced into runtime errors in your page that are frankly quite baffling: Error: Uncaught (in promise): ObjectUnsubscribedError. I know I reached a point where just seeing this error would prompt a reaction similar to this:

If we have that experience in common, read on for a design pattern that may help alleviate this problem, and make your code cleaner at the same time.

But why does this even happen?!

As you might know (or might not), Angular 2 is very heavily reliant on RxJS for its logic flow. It lets Angular 2 avoid costly re-computations by “streaming” or “piping” data from place to place using a system that is neater than callbacks are. You can read a basic overview of it here.

All these “observable sequences” start at something called a Subject — an object that can receive values through an onNext() function and pass them down to any of its subscribers. A Subject can also subscribe to a stream itself, to “pass through” values. Because the Subject can subscribe, it makes sense for it to have an unsubscribe() function to signal it’s done, right? And if it’s done, it should throw an error if it’s told to do anything with more data, right? That’s what being “done” means, after all.

That is exactly what happens. The outcome is our friend ObjectUnsubscribedError. This is entirely reasonable, and when it happens in the middle of regular Javascript (or Typescript) code, the error stack trace can be at least remotely useful at tracking down the exact offending line. However, when it happens in a template, the crash will trace to somewhere in the middle of the Angular 2 framework, leading to a super crappy traceback.

What can I do about it?

Embrace RxJS’s reactive programming style, and Angular 2’s support for it!

Suppose you had this component to display a list of users and their ages, sorted by age:

This works, kind of. It’s even the type of approach that the Angular tutorials would have us use: set up the component using the OnInit hook, and tear it down with the OnDestroy hook. However, some oddities can happen when we manually manage that subscription, which can lead to an elusive and painful ObjectUnsubscribedError. Plus, what happens if you have to manage many subscriptions? Do you use an array of subscriptions? It can get super messy.

I got tired of this approach, so I looked for a better way. I found one. It’s called the Async Pipe, and its usefulness is drastically underestimated. Here’s what it does:

You can use it in a template as you would use any other pipe: {{value | async}} or [property]="value | async". It expects that value is an Observable. When the page is rendered, it sets up its own subscription to that Observable, and gives the template the latest value that came through. When the component is dismantled, it automatically and transparently takes care of cleanup and unsubscribing.

So how does this help our above example? Like this:

In the component itself, we set up a pipeline for receiving users from the user service, copying the array, and sorting it. However, we are never actually subscribing to the Observable we created. It’s declarative, in a sense: we’re telling the browser that users$ is obtained by taking an array of users from the user service, copying it, then sorting it. We don’t ever (explicitly) tell the browser to do that because we don’t have to.

Later, when the the template uses sortedUsers$ | async, Angular 2’s template system knows to grab that Observable and actually subscribe to it — which runs the whole chain of events and gives it the value to use in that statement. When and how the subscription happens, or how it’s torn down, is left to the framework itself.

Feels good to make complexities someone else’s job, doesn’t it?

The result is cleaner, more readable code that is ObjectUnsubscribedError-proof. Not only that, but it is extremely applicable to using a Redux design pattern with @ngrx/store, or similar.

I hope this helps keep your code clean and bug-free!

--

--