Skip to content

Commit 4312af5

Browse files
authored
fix(asapScheduler): resolved memory leak (#5183)
Registered handlers would sometimes leak in memory, this resolves that issue and adds a test. Related #5016
1 parent 2c2f68c commit 4312af5

File tree

2 files changed

+40
-10
lines changed

2 files changed

+40
-10
lines changed

spec/util/Immediate-spec.ts

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { expect } from 'chai';
2-
import { Immediate } from 'rxjs/util/Immediate';
2+
import { Immediate, TestTools } from 'rxjs/internal/util/Immediate';
33

44
describe('Immediate', () => {
55
it('should schedule on the next microtask', (done) => {
@@ -30,4 +30,16 @@ describe('Immediate', () => {
3030
done();
3131
});
3232
});
33+
34+
it('should clear the task after execution', (done) => {
35+
const results: number[] = [];
36+
Immediate.setImmediate(() => results.push(1));
37+
Immediate.setImmediate(() => results.push(2));
38+
39+
setTimeout(() => {
40+
const number = TestTools.pending();
41+
expect(number).to.equal(0);
42+
done();
43+
});
44+
});
3345
});

src/internal/util/Immediate.ts

Lines changed: 27 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,23 +1,41 @@
11
let nextHandle = 1;
2+
const RESOLVED = (() => Promise.resolve())();
3+
const activeHandles: { [key: number]: any } = {};
24

3-
const tasksByHandle: { [handle: string]: () => void } = {};
4-
5-
function runIfPresent(handle: number) {
6-
const cb = tasksByHandle[handle];
7-
if (cb) {
8-
cb();
5+
/**
6+
* Finds the handle in the list of active handles, and removes it.
7+
* Returns `true` if found, `false` otherwise. Used both to clear
8+
* Immediate scheduled tasks, and to identify if a task should be scheduled.
9+
*/
10+
function findAndClearHandle(handle: number): boolean {
11+
if (handle in activeHandles) {
12+
delete activeHandles[handle];
13+
return true;
914
}
15+
return false;
1016
}
1117

18+
/**
19+
* Helper functions to schedule and unschedule microtasks.
20+
*/
1221
export const Immediate = {
1322
setImmediate(cb: () => void): number {
1423
const handle = nextHandle++;
15-
tasksByHandle[handle] = cb;
16-
Promise.resolve().then(() => runIfPresent(handle));
24+
activeHandles[handle] = true;
25+
RESOLVED.then(() => findAndClearHandle(handle) && cb());
1726
return handle;
1827
},
1928

2029
clearImmediate(handle: number): void {
21-
delete tasksByHandle[handle];
30+
findAndClearHandle(handle);
2231
},
2332
};
33+
34+
/**
35+
* Used for internal testing purposes only. Do not export from library.
36+
*/
37+
export const TestTools = {
38+
pending() {
39+
return Object.keys(activeHandles).length;
40+
}
41+
};

0 commit comments

Comments
 (0)