Skip to content

Commit 25ebc13

Browse files
committed
refactor: Replace Zone.scheduleMacroTask with ExperimentalPendingTasks
1 parent 19d3a34 commit 25ebc13

File tree

1 file changed

+27
-47
lines changed

1 file changed

+27
-47
lines changed

src/zones.ts

Lines changed: 27 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,10 @@
11
/* eslint-disable @typescript-eslint/ban-ts-comment */
2-
import { Injectable, NgZone } from "@angular/core";
2+
import {
3+
ExperimentalPendingTasks,
4+
Injectable,
5+
NgZone,
6+
inject,
7+
} from "@angular/core";
38
import {
49
Observable,
510
Operator,
@@ -48,17 +53,16 @@ export class ɵZoneScheduler implements SchedulerLike {
4853
}
4954

5055
class BlockUntilFirstOperator<T> implements Operator<T, T> {
51-
// @ts-ignore
52-
private task: MacroTask | null = null;
53-
54-
constructor(private zone: any) {}
56+
constructor(
57+
private zone: any,
58+
private pendingTasks: ExperimentalPendingTasks
59+
) {}
5560

5661
call(subscriber: Subscriber<T>, source: Observable<T>): TeardownLogic {
57-
const unscheduleTask = this.unscheduleTask.bind(this);
58-
// @ts-ignore
59-
this.task = this.zone.run(() =>
60-
Zone.current.scheduleMacroTask("firebaseZoneBlock", noop, {}, noop, noop)
61-
);
62+
const taskDone = this.zone.run(() => this.pendingTasks.add());
63+
// maybe this is a race condition, invoke in a timeout
64+
// hold for 10ms while I try to figure out what is going on
65+
const unscheduleTask = () => setTimeout(taskDone, 10);
6266

6367
return source
6468
.pipe(
@@ -71,17 +75,6 @@ class BlockUntilFirstOperator<T> implements Operator<T, T> {
7175
.subscribe(subscriber)
7276
.add(unscheduleTask);
7377
}
74-
75-
private unscheduleTask() {
76-
// maybe this is a race condition, invoke in a timeout
77-
// hold for 10ms while I try to figure out what is going on
78-
setTimeout(() => {
79-
if (this.task != null && this.task.state === "scheduled") {
80-
this.task.invoke();
81-
this.task = null;
82-
}
83-
}, 10);
84-
}
8578
}
8679

8780
@Injectable({
@@ -90,6 +83,7 @@ class BlockUntilFirstOperator<T> implements Operator<T, T> {
9083
export class ɵAngularFireSchedulers {
9184
public readonly outsideAngular: ɵZoneScheduler;
9285
public readonly insideAngular: ɵZoneScheduler;
86+
public readonly pendingTasks = inject(ExperimentalPendingTasks);
9387

9488
constructor(public ngZone: NgZone) {
9589
// @ts-ignore
@@ -149,7 +143,9 @@ export function ɵkeepUnstableUntilFirstFactory(
149143
return function keepUnstableUntilFirst<T>(
150144
obs$: Observable<T>
151145
): Observable<T> {
152-
obs$ = obs$.lift(new BlockUntilFirstOperator(schedulers.ngZone));
146+
obs$ = obs$.lift(
147+
new BlockUntilFirstOperator(schedulers.ngZone, schedulers.pendingTasks)
148+
);
153149

154150
return obs$.pipe(
155151
// Run the subscribe body outside of Angular (e.g. calling Firebase SDK to add a listener to a change event)
@@ -165,19 +161,15 @@ export function ɵkeepUnstableUntilFirstFactory(
165161
// @ts-ignore
166162
const zoneWrapFn = (
167163
it: (...args: any[]) => any,
168-
macrotask: MacroTask | undefined
164+
taskDone: VoidFunction | undefined
169165
) => {
170166
// eslint-disable-next-line @typescript-eslint/no-this-alias
171167
const _this = this;
172168
// function() is needed for the arguments object
173169
return function () {
174170
const _arguments = arguments;
175-
if (macrotask) {
176-
setTimeout(() => {
177-
if (macrotask.state === "scheduled") {
178-
macrotask.invoke();
179-
}
180-
}, 10);
171+
if (taskDone) {
172+
setTimeout(taskDone, 10);
181173
}
182174
return run(() => it.apply(_this, _arguments));
183175
};
@@ -187,26 +179,18 @@ export const ɵzoneWrap = <T = unknown>(it: T, blockUntilFirst: boolean): T => {
187179
// function() is needed for the arguments object
188180
return function () {
189181
// @ts-ignore
190-
let macrotask: MacroTask | undefined;
182+
let taskDone: VoidFunction | undefined;
191183
const _arguments = arguments;
192-
// if this is a callback function, e.g, onSnapshot, we should create a microtask and invoke it
184+
// if this is a callback function, e.g, onSnapshot, we should create a pending task and complete it
193185
// only once one of the callback functions is tripped.
194186
for (let i = 0; i < arguments.length; i++) {
195187
if (typeof _arguments[i] === "function") {
196188
if (blockUntilFirst) {
197189
// @ts-ignore
198-
macrotask ||= run(() =>
199-
Zone.current.scheduleMacroTask(
200-
"firebaseZoneBlock",
201-
noop,
202-
{},
203-
noop,
204-
noop
205-
)
206-
);
190+
taskDone ||= run(() => getSchedulers().pendingTasks.add());
207191
}
208192
// TODO create a microtask to track callback functions
209-
_arguments[i] = zoneWrapFn(_arguments[i], macrotask);
193+
_arguments[i] = zoneWrapFn(_arguments[i], taskDone);
210194
}
211195
}
212196
const ret = runOutsideAngular(() => (it as any).apply(this, _arguments));
@@ -234,15 +218,11 @@ export const ɵzoneWrap = <T = unknown>(it: T, blockUntilFirst: boolean): T => {
234218
)
235219
)
236220
);
237-
} else if (typeof ret === "function" && macrotask) {
221+
} else if (typeof ret === "function" && taskDone) {
238222
// Handle unsubscribe
239223
// function() is needed for the arguments object
240224
return function () {
241-
setTimeout(() => {
242-
if (macrotask && macrotask.state === "scheduled") {
243-
macrotask.invoke();
244-
}
245-
}, 10);
225+
setTimeout(taskDone, 10);
246226
return ret.apply(this, arguments);
247227
};
248228
} else {

0 commit comments

Comments
 (0)