diff --git a/src/zones.ts b/src/zones.ts index 2b6a6ffb1..a7149b63b 100644 --- a/src/zones.ts +++ b/src/zones.ts @@ -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, @@ -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, state?: any) => void, delay?: number, state?: any): Subscription { + schedule( + work: (this: SchedulerAction, 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, state: any) { + const workInZone = function (this: SchedulerAction, state: any) { targetZone.runGuarded(() => { work.apply(this, [state]); }); @@ -49,24 +51,32 @@ class BlockUntilFirstOperator implements Operator { // @ts-ignore private task: MacroTask | null = null; - constructor(private zone: any) { - } + constructor(private zone: any) {} call(subscriber: Subscriber, source: Observable): 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; } @@ -75,7 +85,7 @@ class BlockUntilFirstOperator implements Operator { } @Injectable({ - providedIn: 'root', + providedIn: "root", }) export class ɵAngularFireSchedulers { public readonly outsideAngular: ɵZoneScheduler; @@ -83,19 +93,26 @@ export class ɵAngularFireSchedulers { 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; } @@ -126,11 +143,13 @@ export function keepUnstableUntilFirst(obs$: Observable): Observable { * 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(obs$: Observable): Observable { - obs$ = obs$.lift( - new BlockUntilFirstOperator(schedulers.ngZone) - ); +export function ɵkeepUnstableUntilFirstFactory( + schedulers: ɵAngularFireSchedulers +) { + return function keepUnstableUntilFirst( + obs$: Observable + ): Observable { + 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) @@ -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); @@ -161,19 +183,27 @@ const zoneWrapFn = (it: (...args: any[]) => any, macrotask: MacroTask|undefined) }; }; -export const ɵzoneWrap = (it: T, blockUntilFirst: boolean): T => { +export const ɵzoneWrap = (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); @@ -185,7 +215,7 @@ export const ɵzoneWrap = (it: T, blockUntilFirst: boolean): T => { const schedulers = getSchedulers(); return ret.pipe( subscribeOn(schedulers.outsideAngular), - observeOn(schedulers.insideAngular), + observeOn(schedulers.insideAngular) ); } else { return run(() => ret); @@ -195,13 +225,21 @@ export const ɵzoneWrap = (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);