1
1
/* eslint-disable @typescript-eslint/ban-ts-comment */
2
- import { Injectable , NgZone } from '@angular/core' ;
2
+ import {
3
+ ExperimentalPendingTasks ,
4
+ Injectable ,
5
+ NgZone
6
+ } from "@angular/core" ;
3
7
import {
4
8
Observable ,
5
9
Operator ,
@@ -46,32 +50,21 @@ export class ɵZoneScheduler implements SchedulerLike {
46
50
}
47
51
48
52
class BlockUntilFirstOperator < T > implements Operator < T , T > {
49
- // @ts -ignore
50
- private task : MacroTask | null = null ;
51
-
52
- constructor ( private zone : any ) {
53
- }
53
+ constructor (
54
+ private zone : any ,
55
+ private pendingTasks : ExperimentalPendingTasks
56
+ ) { }
54
57
55
58
call ( subscriber : Subscriber < T > , source : Observable < T > ) : TeardownLogic {
56
- const unscheduleTask = this . unscheduleTask . bind ( this ) ;
57
- // @ts -ignore
58
- this . task = this . zone . run ( ( ) => Zone . current . scheduleMacroTask ( 'firebaseZoneBlock' , noop , { } , noop , noop ) ) ;
59
+ const taskDone = this . zone . run ( ( ) => this . pendingTasks . add ( ) ) ;
60
+ // maybe this is a race condition, invoke in a timeout
61
+ // hold for 10ms while I try to figure out what is going on
62
+ const unscheduleTask = ( ) => setTimeout ( taskDone , 10 ) ;
59
63
60
64
return source . pipe (
61
65
tap ( { next : unscheduleTask , complete : unscheduleTask , error : unscheduleTask } )
62
66
) . subscribe ( subscriber ) . add ( unscheduleTask ) ;
63
67
}
64
-
65
- private unscheduleTask ( ) {
66
- // maybe this is a race condition, invoke in a timeout
67
- // hold for 10ms while I try to figure out what is going on
68
- setTimeout ( ( ) => {
69
- if ( this . task != null && this . task . state === 'scheduled' ) {
70
- this . task . invoke ( ) ;
71
- this . task = null ;
72
- }
73
- } , 10 ) ;
74
- }
75
68
}
76
69
77
70
@Injectable ( {
@@ -81,11 +74,15 @@ export class ɵAngularFireSchedulers {
81
74
public readonly outsideAngular : ɵZoneScheduler ;
82
75
public readonly insideAngular : ɵZoneScheduler ;
83
76
84
- constructor ( public ngZone : NgZone ) {
85
- // @ts -ignore
86
- this . outsideAngular = ngZone . runOutsideAngular ( ( ) => new ɵZoneScheduler ( Zone . current ) ) ;
87
- // @ts -ignore
88
- this . insideAngular = ngZone . run ( ( ) => new ɵZoneScheduler ( Zone . current , asyncScheduler ) ) ;
77
+ constructor ( public ngZone : NgZone , public pendingTasks : ExperimentalPendingTasks ) {
78
+ this . outsideAngular = ngZone . runOutsideAngular (
79
+ // @ts -ignore
80
+ ( ) => new ɵZoneScheduler ( Zone . current )
81
+ ) ;
82
+ this . insideAngular = ngZone . run (
83
+ // @ts -ignore
84
+ ( ) => new ɵZoneScheduler ( Zone . current , asyncScheduler )
85
+ ) ;
89
86
globalThis . ɵAngularFireScheduler ||= this ;
90
87
}
91
88
}
@@ -126,10 +123,14 @@ export function keepUnstableUntilFirst<T>(obs$: Observable<T>): Observable<T> {
126
123
* value from firebase but doesn't block the zone forever since the firebase subscription
127
124
* is still alive.
128
125
*/
129
- export function ɵkeepUnstableUntilFirstFactory ( schedulers : ɵAngularFireSchedulers ) {
130
- return function keepUnstableUntilFirst < T > ( obs$ : Observable < T > ) : Observable < T > {
126
+ export function ɵkeepUnstableUntilFirstFactory (
127
+ schedulers : ɵAngularFireSchedulers
128
+ ) {
129
+ return function keepUnstableUntilFirst < T > (
130
+ obs$ : Observable < T >
131
+ ) : Observable < T > {
131
132
obs$ = obs$ . lift (
132
- new BlockUntilFirstOperator ( schedulers . ngZone )
133
+ new BlockUntilFirstOperator ( schedulers . ngZone , schedulers . pendingTasks )
133
134
) ;
134
135
135
136
return obs$ . pipe (
@@ -144,39 +145,36 @@ export function ɵkeepUnstableUntilFirstFactory(schedulers: ɵAngularFireSchedul
144
145
}
145
146
146
147
// @ts -ignore
147
- const zoneWrapFn = ( it : ( ...args : any [ ] ) => any , macrotask : MacroTask | undefined ) => {
148
+ const zoneWrapFn = (
149
+ it : ( ...args : any [ ] ) => any ,
150
+ taskDone : VoidFunction | undefined
151
+ ) => {
148
152
// eslint-disable-next-line @typescript-eslint/no-this-alias
149
153
const _this = this ;
150
154
// function() is needed for the arguments object
151
155
return function ( ) {
152
156
const _arguments = arguments ;
153
- if ( macrotask ) {
154
- setTimeout ( ( ) => {
155
- if ( macrotask . state === 'scheduled' ) {
156
- macrotask . invoke ( ) ;
157
- }
158
- } , 10 ) ;
157
+ if ( taskDone ) {
158
+ setTimeout ( taskDone , 10 ) ;
159
159
}
160
160
return run ( ( ) => it . apply ( _this , _arguments ) ) ;
161
161
} ;
162
162
} ;
163
163
164
164
export const ɵzoneWrap = < T = unknown > ( it : T , blockUntilFirst : boolean ) : T => {
165
165
// function() is needed for the arguments object
166
- return function ( ) {
167
- // @ts -ignore
168
- let macrotask : MacroTask | undefined ;
166
+ return function ( ) {
167
+ let taskDone : VoidFunction | undefined ;
169
168
const _arguments = arguments ;
170
- // if this is a callback function, e.g, onSnapshot, we should create a microtask and invoke it
169
+ // if this is a callback function, e.g, onSnapshot, we should create a pending task and complete it
171
170
// only once one of the callback functions is tripped.
172
171
for ( let i = 0 ; i < arguments . length ; i ++ ) {
173
172
if ( typeof _arguments [ i ] === 'function' ) {
174
173
if ( blockUntilFirst ) {
175
- // @ts -ignore
176
- macrotask ||= run ( ( ) => Zone . current . scheduleMacroTask ( 'firebaseZoneBlock' , noop , { } , noop , noop ) ) ;
174
+ taskDone ||= run ( ( ) => getSchedulers ( ) . pendingTasks . add ( ) ) ;
177
175
}
178
176
// TODO create a microtask to track callback functions
179
- _arguments [ i ] = zoneWrapFn ( _arguments [ i ] , macrotask ) ;
177
+ _arguments [ i ] = zoneWrapFn ( _arguments [ i ] , taskDone ) ;
180
178
}
181
179
}
182
180
const ret = runOutsideAngular ( ( ) => ( it as any ) . apply ( this , _arguments ) ) ;
@@ -195,16 +193,20 @@ export const ɵzoneWrap = <T= unknown>(it: T, blockUntilFirst: boolean): T => {
195
193
return ret . pipe ( keepUnstableUntilFirst ) as any ;
196
194
} else if ( ret instanceof Promise ) {
197
195
// eslint-disable-next-line @typescript-eslint/no-misused-promises
198
- return run ( ( ) => new Promise ( ( resolve , reject ) => ret . then ( it => run ( ( ) => resolve ( it ) ) , reason => run ( ( ) => reject ( reason ) ) ) ) ) ;
199
- } else if ( typeof ret === 'function' && macrotask ) {
196
+ return run (
197
+ ( ) =>
198
+ new Promise ( ( resolve , reject ) =>
199
+ ret . then (
200
+ ( it ) => run ( ( ) => resolve ( it ) ) ,
201
+ ( reason ) => run ( ( ) => reject ( reason ) )
202
+ )
203
+ )
204
+ ) ;
205
+ } else if ( typeof ret === "function" && taskDone ) {
200
206
// Handle unsubscribe
201
207
// function() is needed for the arguments object
202
- return function ( ) {
203
- setTimeout ( ( ) => {
204
- if ( macrotask && macrotask . state === 'scheduled' ) {
205
- macrotask . invoke ( ) ;
206
- }
207
- } , 10 ) ;
208
+ return function ( ) {
209
+ setTimeout ( taskDone , 10 ) ;
208
210
return ret . apply ( this , arguments ) ;
209
211
} ;
210
212
} else {
0 commit comments