Skip to content

Commit 15c781c

Browse files
Using a plain array to store the handles from setTimeout()
1 parent f063b61 commit 15c781c

File tree

1 file changed

+34
-17
lines changed

1 file changed

+34
-17
lines changed

packages/firestore/src/util/async_queue.ts

Lines changed: 34 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,21 @@ export class AsyncQueue {
2424
// The last promise in the queue.
2525
private tail: Promise<AnyJs | void> = Promise.resolve();
2626

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;
3142

3243
// visible for testing
3344
failure: Error;
@@ -49,19 +60,23 @@ export class AsyncQueue {
4960
}
5061

5162
if ((delay || 0) > 0) {
63+
this.delayedOperationsCount++;
5264
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(() => {
5767
this.scheduleInternal(() => {
5868
return op().then(result => {
5969
deferred.resolve(result);
6070
});
6171
});
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 };
6580
return deferred.promise;
6681
} else {
6782
return this.scheduleInternal(op);
@@ -103,13 +118,15 @@ export class AsyncQueue {
103118
* tasks that are pending with a non-zero delay.
104119
*/
105120
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+
}
111128
});
112-
this.delayedOperations.clear();
129+
this.delayedOperations = [];
113130
return this.schedule(() => Promise.resolve());
114131
}
115132
}

0 commit comments

Comments
 (0)