Skip to content

Commit dbd0fa0

Browse files
atscottAndrewKushnir
authored andcommitted
fix(core): async EventEmitter should contribute to app stability (#56308)
async `EventEmitter` should contribute to app stability. fixes #56290 PR Close #56308
1 parent 5ec24c9 commit dbd0fa0

File tree

15 files changed

+53
-53
lines changed

15 files changed

+53
-53
lines changed

goldens/public-api/core/index.api.md

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -679,9 +679,7 @@ export interface ExistingSansProvider {
679679
export class ExperimentalPendingTasks {
680680
add(): () => void;
681681
// (undocumented)
682-
static ɵfac: i0.ɵɵFactoryDeclaration<ExperimentalPendingTasks, never>;
683-
// (undocumented)
684-
static ɵprov: i0.ɵɵInjectableDeclaration<ExperimentalPendingTasks>;
682+
static ɵprov: unknown;
685683
}
686684

687685
// @public

packages/core/src/event_emitter.ts

Lines changed: 17 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import {OutputRef} from './authoring/output/output_ref';
1313
import {isInInjectionContext} from './di/contextual';
1414
import {inject} from './di/injector_compatibility';
1515
import {DestroyRef} from './linker/destroy_ref';
16+
import {PendingTasks} from './pending_tasks';
1617

1718
/**
1819
* Use in components with the `@Output` directive to emit custom events
@@ -111,6 +112,7 @@ export interface EventEmitter<T> extends Subject<T>, OutputRef<T> {
111112
class EventEmitter_ extends Subject<any> implements OutputRef<any> {
112113
__isAsync: boolean; // tslint:disable-line
113114
destroyRef: DestroyRef | undefined = undefined;
115+
private readonly pendingTasks: PendingTasks | undefined = undefined;
114116

115117
constructor(isAsync: boolean = false) {
116118
super();
@@ -120,6 +122,7 @@ class EventEmitter_ extends Subject<any> implements OutputRef<any> {
120122
// For backwards compatibility reasons, this cannot be required
121123
if (isInInjectionContext()) {
122124
this.destroyRef = inject(DestroyRef, {optional: true}) ?? undefined;
125+
this.pendingTasks = inject(PendingTasks);
123126
}
124127
}
125128

@@ -145,14 +148,14 @@ class EventEmitter_ extends Subject<any> implements OutputRef<any> {
145148
}
146149

147150
if (this.__isAsync) {
148-
errorFn = _wrapInTimeout(errorFn);
151+
errorFn = this.wrapInTimeout(errorFn);
149152

150153
if (nextFn) {
151-
nextFn = _wrapInTimeout(nextFn);
154+
nextFn = this.wrapInTimeout(nextFn);
152155
}
153156

154157
if (completeFn) {
155-
completeFn = _wrapInTimeout(completeFn);
158+
completeFn = this.wrapInTimeout(completeFn);
156159
}
157160
}
158161

@@ -164,12 +167,18 @@ class EventEmitter_ extends Subject<any> implements OutputRef<any> {
164167

165168
return sink;
166169
}
167-
}
168170

169-
function _wrapInTimeout(fn: (value: unknown) => any) {
170-
return (value: unknown) => {
171-
setTimeout(fn, undefined, value);
172-
};
171+
private wrapInTimeout(fn: (value: unknown) => any) {
172+
return (value: unknown) => {
173+
const taskId = this.pendingTasks?.add();
174+
setTimeout(() => {
175+
fn(value);
176+
if (taskId !== undefined) {
177+
this.pendingTasks?.remove(taskId);
178+
}
179+
});
180+
};
181+
}
173182
}
174183

175184
/**

packages/core/src/pending_tasks.ts

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -8,16 +8,13 @@
88

99
import {BehaviorSubject} from 'rxjs';
1010

11-
import {inject} from './di';
12-
import {Injectable} from './di/injectable';
11+
import {inject} from './di/injector_compatibility';
12+
import {ɵɵdefineInjectable} from './di/interface/defs';
1313
import {OnDestroy} from './interface/lifecycle_hooks';
1414

1515
/**
1616
* Internal implementation of the pending tasks service.
1717
*/
18-
@Injectable({
19-
providedIn: 'root',
20-
})
2118
export class PendingTasks implements OnDestroy {
2219
private taskId = 0;
2320
private pendingTasks = new Set<number>();
@@ -48,6 +45,13 @@ export class PendingTasks implements OnDestroy {
4845
this.hasPendingTasks.next(false);
4946
}
5047
}
48+
49+
/** @nocollapse */
50+
static ɵprov = /** @pureOrBreakMyCode */ ɵɵdefineInjectable({
51+
token: PendingTasks,
52+
providedIn: 'root',
53+
factory: () => new PendingTasks(),
54+
});
5155
}
5256

5357
/**
@@ -76,9 +80,6 @@ export class PendingTasks implements OnDestroy {
7680
* @publicApi
7781
* @experimental
7882
*/
79-
@Injectable({
80-
providedIn: 'root',
81-
})
8283
export class ExperimentalPendingTasks {
8384
private internalPendingTasks = inject(PendingTasks);
8485
/**
@@ -89,4 +90,11 @@ export class ExperimentalPendingTasks {
8990
const taskId = this.internalPendingTasks.add();
9091
return () => this.internalPendingTasks.remove(taskId);
9192
}
93+
94+
/** @nocollapse */
95+
static ɵprov = /** @pureOrBreakMyCode */ ɵɵdefineInjectable({
96+
token: ExperimentalPendingTasks,
97+
providedIn: 'root',
98+
factory: () => new ExperimentalPendingTasks(),
99+
});
92100
}

packages/core/test/bundling/animations-standalone/bundle.golden_symbols.json

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -629,9 +629,6 @@
629629
{
630630
"name": "_wasLastNodeCreated"
631631
},
632-
{
633-
"name": "_wrapInTimeout"
634-
},
635632
{
636633
"name": "activeConsumer"
637634
},

packages/core/test/bundling/animations/bundle.golden_symbols.json

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -686,9 +686,6 @@
686686
{
687687
"name": "_wasLastNodeCreated"
688688
},
689-
{
690-
"name": "_wrapInTimeout"
691-
},
692689
{
693690
"name": "activeConsumer"
694691
},

packages/core/test/bundling/cyclic_import/bundle.golden_symbols.json

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -515,9 +515,6 @@
515515
{
516516
"name": "_wasLastNodeCreated"
517517
},
518-
{
519-
"name": "_wrapInTimeout"
520-
},
521518
{
522519
"name": "activeConsumer"
523520
},

packages/core/test/bundling/defer/bundle.golden_symbols.json

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -581,9 +581,6 @@
581581
{
582582
"name": "_wasLastNodeCreated"
583583
},
584-
{
585-
"name": "_wrapInTimeout"
586-
},
587584
{
588585
"name": "activeConsumer"
589586
},

packages/core/test/bundling/forms_reactive/bundle.golden_symbols.json

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -716,9 +716,6 @@
716716
{
717717
"name": "_wasLastNodeCreated"
718718
},
719-
{
720-
"name": "_wrapInTimeout"
721-
},
722719
{
723720
"name": "activeConsumer"
724721
},

packages/core/test/bundling/forms_template_driven/bundle.golden_symbols.json

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -698,9 +698,6 @@
698698
{
699699
"name": "_wasLastNodeCreated"
700700
},
701-
{
702-
"name": "_wrapInTimeout"
703-
},
704701
{
705702
"name": "activeConsumer"
706703
},

packages/core/test/bundling/hello_world/bundle.golden_symbols.json

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -389,9 +389,6 @@
389389
{
390390
"name": "_retrieveHydrationInfoImpl"
391391
},
392-
{
393-
"name": "_wrapInTimeout"
394-
},
395392
{
396393
"name": "activeConsumer"
397394
},

0 commit comments

Comments
 (0)