@@ -24,10 +24,21 @@ export class AsyncQueue {
24
24
// The last promise in the queue.
25
25
private tail : Promise < AnyJs | void > = Promise . resolve ( ) ;
26
26
27
- // A map of timeout handles to their respective deferred promises. Contains an
28
- // entry for each operation that is still queued to run in the future (i.e. it
29
- // has a delay that has not yet elapsed).
30
- private delayedOperations : Map < number , Deferred < any > > = new Map ( ) ;
27
+ // A list with timeout handles and their respective deferred promises.
28
+ // Contains an entry for each operation that is queued to run future (i.e. it
29
+ // has a delay that has not yet elapsed). Prior to cleanup, this list may also
30
+ // contain entries that have already been run (in which case `handle` is
31
+ // null).
32
+ private delayedOperations : {
33
+ // tslint:disable-next-line:no-any Accept any return type from setTimeout().
34
+ handle : any ;
35
+ deferred : Deferred < AnyJs | void > ;
36
+ } [ ] = [ ] ;
37
+
38
+ // The number of operations that are queued to be run in the future (i.e. they
39
+ // have a delay that has not yet elapsed). Unlike `delayedOperations`, this
40
+ // is guaranteed to only contain operations that have not yet been run.
41
+ private delayedOperationsCount = 0 ;
31
42
32
43
// visible for testing
33
44
failure : Error ;
@@ -49,19 +60,23 @@ export class AsyncQueue {
49
60
}
50
61
51
62
if ( ( delay || 0 ) > 0 ) {
63
+ this . delayedOperationsCount ++ ;
52
64
const deferred = new Deferred < T > ( ) ;
53
- // Note that the Node typings indicate that setTimeout returns a Timer,
54
- // while the browser-based setTimeout() function returns a number. We
55
- // forcefully cast to the number type.
56
- const handle : number = setTimeout ( ( ) => {
65
+ const opIndex = this . delayedOperations . length ;
66
+ const handle = setTimeout ( ( ) => {
57
67
this . scheduleInternal ( ( ) => {
58
68
return op ( ) . then ( result => {
59
69
deferred . resolve ( result ) ;
60
70
} ) ;
61
71
} ) ;
62
- this . delayedOperations . delete ( handle ) ;
63
- } , delay ) as any ;
64
- this . delayedOperations . set ( handle , deferred ) ;
72
+ this . delayedOperationsCount -- ;
73
+ if ( this . delayedOperationsCount > 0 ) {
74
+ this . delayedOperations [ opIndex ] . handle = null ;
75
+ } else {
76
+ this . delayedOperations = [ ] ;
77
+ }
78
+ } , delay ) ;
79
+ this . delayedOperations [ opIndex ] = { handle, deferred } ;
65
80
return deferred . promise ;
66
81
} else {
67
82
return this . scheduleInternal ( op ) ;
@@ -103,13 +118,15 @@ export class AsyncQueue {
103
118
* tasks that are pending with a non-zero delay.
104
119
*/
105
120
drain ( ) : Promise < void > {
106
- this . delayedOperations . forEach ( ( deferred , handle ) => {
107
- clearTimeout ( handle ) ;
108
- deferred . reject (
109
- new FirestoreError ( Code . CANCELLED , 'Operation cancelled by shutdown' )
110
- ) ;
121
+ this . delayedOperations . forEach ( entry => {
122
+ if ( entry . handle ) {
123
+ clearTimeout ( entry . handle ) ;
124
+ entry . deferred . reject (
125
+ new FirestoreError ( Code . CANCELLED , 'Operation cancelled by shutdown' )
126
+ ) ;
127
+ }
111
128
} ) ;
112
- this . delayedOperations . clear ( ) ;
129
+ this . delayedOperations = [ ] ;
113
130
return this . schedule ( ( ) => Promise . resolve ( ) ) ;
114
131
}
115
132
}
0 commit comments