Skip to content

Commit

Permalink
format zones.ts
Browse files Browse the repository at this point in the history
  • Loading branch information
mmalerba committed May 31, 2024
1 parent 3639e41 commit 19d3a34
Showing 1 changed file with 78 additions and 40 deletions.
118 changes: 78 additions & 40 deletions src/zones.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/* eslint-disable @typescript-eslint/ban-ts-comment */
import { Injectable, NgZone } from '@angular/core';
import { Injectable, NgZone } from "@angular/core";
import {
Observable,
Operator,
Expand All @@ -9,30 +9,32 @@ import {
Subscription,
TeardownLogic,
asyncScheduler,
queueScheduler
} from 'rxjs';
import { observeOn, subscribeOn, tap } from 'rxjs/operators';
queueScheduler,
} from "rxjs";
import { observeOn, subscribeOn, tap } from "rxjs/operators";

// eslint-disable-next-line @typescript-eslint/no-empty-function
function noop() {
}
function noop() {}

/**
* Schedules tasks so that they are invoked inside the Zone that is passed in the constructor.
*/
export class ɵZoneScheduler implements SchedulerLike {
constructor(private zone: any, private delegate: any = queueScheduler) {
}
constructor(private zone: any, private delegate: any = queueScheduler) {}

now() {
return this.delegate.now();
}

schedule(work: (this: SchedulerAction<any>, state?: any) => void, delay?: number, state?: any): Subscription {
schedule(
work: (this: SchedulerAction<any>, state?: any) => void,
delay?: number,
state?: any
): Subscription {
const targetZone = this.zone;
// Wrap the specified work function to make sure that if nested scheduling takes place the
// work is executed in the correct zone
const workInZone = function(this: SchedulerAction<any>, state: any) {
const workInZone = function (this: SchedulerAction<any>, state: any) {
targetZone.runGuarded(() => {
work.apply(this, [state]);
});
Expand All @@ -49,24 +51,32 @@ class BlockUntilFirstOperator<T> implements Operator<T, T> {
// @ts-ignore
private task: MacroTask | null = null;

constructor(private zone: any) {
}
constructor(private zone: any) {}

call(subscriber: Subscriber<T>, source: Observable<T>): TeardownLogic {
const unscheduleTask = this.unscheduleTask.bind(this);
// @ts-ignore
this.task = this.zone.run(() => Zone.current.scheduleMacroTask('firebaseZoneBlock', noop, {}, noop, noop));
this.task = this.zone.run(() =>
Zone.current.scheduleMacroTask("firebaseZoneBlock", noop, {}, noop, noop)
);

return source.pipe(
tap({ next: unscheduleTask, complete: unscheduleTask, error: unscheduleTask })
).subscribe(subscriber).add(unscheduleTask);
return source
.pipe(
tap({
next: unscheduleTask,
complete: unscheduleTask,
error: unscheduleTask,
})
)
.subscribe(subscriber)
.add(unscheduleTask);
}

private unscheduleTask() {
// maybe this is a race condition, invoke in a timeout
// hold for 10ms while I try to figure out what is going on
setTimeout(() => {
if (this.task != null && this.task.state === 'scheduled') {
if (this.task != null && this.task.state === "scheduled") {
this.task.invoke();
this.task = null;
}
Expand All @@ -75,27 +85,34 @@ class BlockUntilFirstOperator<T> implements Operator<T, T> {
}

@Injectable({
providedIn: 'root',
providedIn: "root",
})
export class ɵAngularFireSchedulers {
public readonly outsideAngular: ɵZoneScheduler;
public readonly insideAngular: ɵZoneScheduler;

constructor(public ngZone: NgZone) {
// @ts-ignore
this.outsideAngular = ngZone.runOutsideAngular(() => new ɵZoneScheduler(Zone.current));
this.outsideAngular = ngZone.runOutsideAngular(
() => new ɵZoneScheduler(Zone.current)
);
// @ts-ignore
this.insideAngular = ngZone.run(() => new ɵZoneScheduler(Zone.current, asyncScheduler));
this.insideAngular = ngZone.run(
() => new ɵZoneScheduler(Zone.current, asyncScheduler)
);
globalThis.ɵAngularFireScheduler ||= this;
}
}

function getSchedulers() {
const schedulers = globalThis.ɵAngularFireScheduler as ɵAngularFireSchedulers|undefined;
const schedulers = globalThis.ɵAngularFireScheduler as
| ɵAngularFireSchedulers
| undefined;
if (!schedulers) {
throw new Error(
`Either AngularFireModule has not been provided in your AppModule (this can be done manually or implictly using
provideFirebaseApp) or you're calling an AngularFire method outside of an NgModule (which is not supported).`);
`Either AngularFireModule has not been provided in your AppModule (this can be done manually or implictly using
provideFirebaseApp) or you're calling an AngularFire method outside of an NgModule (which is not supported).`
);
}
return schedulers;
}
Expand Down Expand Up @@ -126,11 +143,13 @@ export function keepUnstableUntilFirst<T>(obs$: Observable<T>): Observable<T> {
* value from firebase but doesn't block the zone forever since the firebase subscription
* is still alive.
*/
export function ɵkeepUnstableUntilFirstFactory(schedulers: ɵAngularFireSchedulers) {
return function keepUnstableUntilFirst<T>(obs$: Observable<T>): Observable<T> {
obs$ = obs$.lift(
new BlockUntilFirstOperator(schedulers.ngZone)
);
export function ɵkeepUnstableUntilFirstFactory(
schedulers: ɵAngularFireSchedulers
) {
return function keepUnstableUntilFirst<T>(
obs$: Observable<T>
): Observable<T> {
obs$ = obs$.lift(new BlockUntilFirstOperator(schedulers.ngZone));

return obs$.pipe(
// Run the subscribe body outside of Angular (e.g. calling Firebase SDK to add a listener to a change event)
Expand All @@ -144,15 +163,18 @@ export function ɵkeepUnstableUntilFirstFactory(schedulers: ɵAngularFireSchedul
}

// @ts-ignore
const zoneWrapFn = (it: (...args: any[]) => any, macrotask: MacroTask|undefined) => {
const zoneWrapFn = (
it: (...args: any[]) => any,
macrotask: MacroTask | undefined
) => {
// eslint-disable-next-line @typescript-eslint/no-this-alias
const _this = this;
// function() is needed for the arguments object
return function() {
return function () {
const _arguments = arguments;
if (macrotask) {
setTimeout(() => {
if (macrotask.state === 'scheduled') {
if (macrotask.state === "scheduled") {
macrotask.invoke();
}
}, 10);
Expand All @@ -161,19 +183,27 @@ const zoneWrapFn = (it: (...args: any[]) => any, macrotask: MacroTask|undefined)
};
};

export const ɵzoneWrap = <T= unknown>(it: T, blockUntilFirst: boolean): T => {
export const ɵzoneWrap = <T = unknown>(it: T, blockUntilFirst: boolean): T => {
// function() is needed for the arguments object
return function() {
return function () {
// @ts-ignore
let macrotask: MacroTask | undefined;
const _arguments = arguments;
// if this is a callback function, e.g, onSnapshot, we should create a microtask and invoke it
// only once one of the callback functions is tripped.
for (let i = 0; i < arguments.length; i++) {
if (typeof _arguments[i] === 'function') {
if (typeof _arguments[i] === "function") {
if (blockUntilFirst) {
// @ts-ignore
macrotask ||= run(() => Zone.current.scheduleMacroTask('firebaseZoneBlock', noop, {}, noop, noop));
macrotask ||= run(() =>
Zone.current.scheduleMacroTask(
"firebaseZoneBlock",
noop,
{},
noop,
noop
)
);
}
// TODO create a microtask to track callback functions
_arguments[i] = zoneWrapFn(_arguments[i], macrotask);
Expand All @@ -185,7 +215,7 @@ export const ɵzoneWrap = <T= unknown>(it: T, blockUntilFirst: boolean): T => {
const schedulers = getSchedulers();
return ret.pipe(
subscribeOn(schedulers.outsideAngular),
observeOn(schedulers.insideAngular),
observeOn(schedulers.insideAngular)
);
} else {
return run(() => ret);
Expand All @@ -195,13 +225,21 @@ export const ɵzoneWrap = <T= unknown>(it: T, blockUntilFirst: boolean): T => {
return ret.pipe(keepUnstableUntilFirst) as any;
} else if (ret instanceof Promise) {
// eslint-disable-next-line @typescript-eslint/no-misused-promises
return run(() => new Promise((resolve, reject) => ret.then(it => run(() => resolve(it)), reason => run(() => reject(reason)))));
} else if (typeof ret === 'function' && macrotask) {
return run(
() =>
new Promise((resolve, reject) =>
ret.then(
(it) => run(() => resolve(it)),
(reason) => run(() => reject(reason))
)
)
);
} else if (typeof ret === "function" && macrotask) {
// Handle unsubscribe
// function() is needed for the arguments object
return function() {
return function () {
setTimeout(() => {
if (macrotask && macrotask.state === 'scheduled') {
if (macrotask && macrotask.state === "scheduled") {
macrotask.invoke();
}
}, 10);
Expand Down

0 comments on commit 19d3a34

Please sign in to comment.