Skip to content

Commit 126efc9

Browse files
arturovtthePunderWoman
authored andcommitted
fix(common): cancel reader when app is destroyed (#61528)
Streams left in a pending state (due to `break` without cancel) may continue consuming or holding onto data behind the scenes. Calling `reader.cancel()` allows the browser or the underlying system to release any network or memory resources associated with the stream. PR Close #61528
1 parent c78e75d commit 126efc9

File tree

1 file changed

+17
-0
lines changed

1 file changed

+17
-0
lines changed

packages/common/http/src/fetch.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -148,6 +148,8 @@ export class FetchBackend implements HttpBackend {
148148
// when the zone is nooped.
149149
const reqZone = typeof Zone !== 'undefined' && Zone.current;
150150

151+
let canceled = false;
152+
151153
// Perform response processing outside of Angular zone to
152154
// ensure no excessive change detection runs are executed
153155
// Here calling the async ReadableStreamDefaultReader.read() is responsible for triggering CD
@@ -158,6 +160,12 @@ export class FetchBackend implements HttpBackend {
158160
// This may happen if the app was explicitly destroyed before
159161
// the response returned entirely.
160162
if (this.appRef.destroyed) {
163+
// Streams left in a pending state (due to `break` without cancel) may
164+
// continue consuming or holding onto data behind the scenes.
165+
// Calling `reader.cancel()` allows the browser or the underlying
166+
// system to release any network or memory resources associated with the stream.
167+
await reader.cancel();
168+
canceled = true;
161169
break;
162170
}
163171

@@ -189,6 +197,15 @@ export class FetchBackend implements HttpBackend {
189197
}
190198
});
191199

200+
// We need to manage the canceled state — because the Streams API does not
201+
// expose a direct `.state` property on the reader.
202+
// We need to `return` because `parseBody` may not be able to parse chunks
203+
// that were only partially read (due to cancellation caused by app destruction).
204+
if (canceled) {
205+
observer.complete();
206+
return;
207+
}
208+
192209
// Combine all chunks.
193210
const chunksAll = this.concatChunks(chunks, receivedLength);
194211
try {

0 commit comments

Comments
 (0)