You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
However, same drawbacks here: Cumbersome and obfuscating.
102
+
103
+
## Unsubscribe with ``takeUntil``
104
+
A cleaner way of unsubscribing is using ``takeUntil``.
105
+
106
+
Official docs for ``takeUntil``: ``takeUntil(notifier: Observable<any>)`` — Emits the values emitted by the source Observable until a notifier Observable emits a value.
When the component gets destroyed, the observable ``ngDestroy`` gets completed, causing the subscriptions to complete.
136
+
Thus memory leaks and side effects are avoided.
137
+
138
+
Drawbacks: As with the other methods, it's still quite verbose and error-prone.
139
+
140
+
NOTE: takeUntil operator should always come last (https://blog.angularindepth.com/rxjs-avoiding-takeuntil-leaks-fb5182d047ef)
141
+
142
+
## Using ``untilDestroyed``
143
+
There is an npm package called ``@ngneat/until-destroy`` (https://github.com/ngneat/until-destroy).
144
+
145
+
You can install it (for Angular versions using Ivy) with
146
+
```
147
+
npm install --save @ngneat/until-destroy
148
+
```
149
+
150
+
Or for previous Angular versions with:
151
+
```
152
+
npm install --save ngx-take-until-destroy
153
+
```
154
+
155
+
It simplifies the unsubscription handling a lot. It internally creates a ``Subject`` and uses ``takeUntil``, hooked into ``ngOnDestroy``.
156
+
The above code simplifies to:
157
+
```
158
+
everySecond = timer(0, 1000);
159
+
everyThirdSecond = timer(0, 3000);
160
+
161
+
constructor() {
162
+
}
163
+
164
+
ngOnInit() {
165
+
this.everySecond.pipe(untilDestroyed(this))
166
+
.subscribe(() => {
167
+
// some logic here
168
+
});
169
+
170
+
this.everyThirdSecond.pipe(untilDestroyed(this))
171
+
.subscribe(() => {
172
+
// some logic here
173
+
});
174
+
}
175
+
176
+
ngOnDestroy() {
177
+
// needed for untilDestroyed
178
+
}
179
+
```
180
+
Only drawback: We have to implement ``ngOnDestroy`` everywhere we want to use ``untilDestroyed``.
181
+
However it should be easy to write a linting rule to enforce this.
182
+
183
+
184
+
# Examples with further explanations
66
185
## How to run the examples by yourself
67
186
The project was set up using the latest [Angular CLI](https://github.com/angular/angular-cli) (version 8.3.6). Therefore you can just clone the repository and run
68
187
``
@@ -74,15 +193,15 @@ angular-app
74
193
``
75
194
folder.
76
195
77
-
## The questions and the study method
78
-
We want to answer the following questions in each case study:
196
+
## The questions
197
+
We want to answer the following questions:
79
198
80
199
### 1. Can we run into the problem of unwanted side effects?
81
-
To investigate this issue, we use the scenario, that we want to set the document title (inside the component we navigated to)
200
+
To investigate this issue, we use the scenario that we want to set the document title (inside the component we navigated to)
82
201
dynamically on behalf of a observable.
83
202
To this end, we use the Angular title service (https://angular.io/guide/set-document-title).
84
203
85
-
In each experiment, we set the title in the observables callback inside component ``XY``, navigate to the ``EmptyComponent``
204
+
In each experiment, we set the title in the observables callback inside component ``XY``, then navigate to the ``EmptyComponent``
86
205
(which does nothing else than setting its own title), and observe if the title still gets updated.
87
206
88
207
### 2. Can we run into the problem of memory leaks?
@@ -125,16 +244,6 @@ In fact, by navigating back and forth between ``EmptyComponent`` and ``RxjsTimer
125
244
NOTE: If you don't use any references (like member variables) from the component in the callback,
126
245
the component gets garbage collected and there is no memory leak.
127
246
128
-
### Countermeasures
129
-
One countermeasure (among others at the end of this readme) is to manually unsubscribe in ``ngOnDestroy``.
130
-
```
131
-
ngOnDestroy() {
132
-
// Avoid side effects and memory leak by unsubscribing:
133
-
this.subscription.unsubscribe();
134
-
}
135
-
```
136
-
With this statement, the side effects and the memory leak are avoided.
137
-
138
247
### Summary
139
248
__ALWAYS unsubscribe if the observable does not complete or if you are not sure if it completes,
140
249
since the callback logic still runs in the background otherwise,
@@ -170,16 +279,6 @@ No matter if we navigate before the observable completes or afterwards -
170
279
the component ``RxjsTimerComplete`` always eventually gets garbage collected after it completed.
171
280
So there is no memory leak.
172
281
173
-
### Countermeasures
174
-
One countermeasure to avoid possible side effects (among others at the end of this readme) is to manually unsubscribe in ``ngOnDestroy``.
175
-
```
176
-
ngOnDestroy() {
177
-
// Avoid side effects by unsubscribing:
178
-
this.subscription.unsubscribe();
179
-
}
180
-
```
181
-
With this statement, the side effects and the memory leak are avoided.
182
-
183
282
### Summary
184
283
__ALWAYS unsubscribe if you execute code with side effects in your callback.__
185
284
@@ -229,16 +328,6 @@ Are possible due to network latencies. The callback still gets executed, even if
229
328
#### Memory leak
230
329
After the call completes the component gets garbage collected (even if navigated to another component).
231
330
232
-
### Countermeasures
233
-
One countermeasure to avoid possible side effects (amongst others at the end of this readme) is to manually unsubscribe in ``ngOnDestroy``.
234
-
```
235
-
ngOnDestroy() {
236
-
// Avoid side effects by unsubscribing:
237
-
this.subscription.unsubscribe();
238
-
}
239
-
```
240
-
With this statement, the side effects and the memory leak are avoided.
241
-
242
331
### Summary
243
332
__ALWAYS unsubscribe if you execute code with side effects in your callback.__
244
333
Many get that wrong, since they have almost no latency in their network calls when testing their application.
@@ -311,117 +400,6 @@ As expected, only the component with the timer does not get garbage collected.
311
400
This is good news, as this means the third party components (as used as in this example)
312
401
cannot affect memory leaks on your components.
313
402
314
-
# Recommended ways to unsubscribe
315
-
The obvious way of unsubscribing is how it is done in our examples: Assign the subscription to a class
316
-
property and unsubscribe in the ``ngOnDestroy()`` method.
317
-
318
-
However, its not feasible for large applications with many subscriptions,
319
-
as its cumbersome to write and introduces a lot of obfuscating code.
320
-
321
-
Another way of handling it, would be to collect all subscriptions and unsubscribe them at once:
322
-
```
323
-
private readonly subscription = new Subscription();
However, same drawbacks here: Cumbersome and obfuscating.
345
-
346
-
## Unsubscribe with ``takeUntil``
347
-
A cleaner way of unsubscribing is using ``takeUntil``.
348
-
349
-
Official docs for ``takeUntil``: ``takeUntil(notifier: Observable<any>)`` — Emits the values emitted by the source Observable until a notifier Observable emits a value.
0 commit comments