-
-
Notifications
You must be signed in to change notification settings - Fork 179
/
AsyncTaskManager.ts
126 lines (113 loc) · 2.65 KB
/
AsyncTaskManager.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
/**
* Handles async tasks.
*/
export default class AsyncTaskManager {
private static taskID = 0;
private runningTasks: { [k: string]: () => void } = {};
private runningTimers: NodeJS.Timeout[] = [];
private queue: { resolve: () => void; reject: (error: Error) => void }[] = [];
/**
* Returns a promise that is fulfilled when async tasks are complete.
* This method is not part of the HTML standard.
*
* @returns Promise.
*/
public async whenComplete(): Promise<void> {
return new Promise((resolve, reject) => {
const timerID = global.setTimeout(() => {
this.endTimer(timerID);
}, 0);
this.startTimer(timerID);
this.queue.push({ resolve, reject });
});
}
/**
* Ends all tasks.
*
* @param [error] Error.
*/
public cancelAll(error?: Error): void {
const runningTimers = this.runningTimers;
const runningTasks = this.runningTasks;
const promises = this.queue;
this.runningTasks = {};
this.runningTimers = [];
this.queue = [];
for (const timer of runningTimers) {
global.clearTimeout(timer);
}
for (const key of Object.keys(runningTasks)) {
runningTasks[key]();
}
for (const promise of promises) {
if (error) {
promise.reject(error);
} else {
promise.resolve();
}
}
}
/**
* Starts a timer.
*
* @param timerID Timer ID.
*/
public startTimer(timerID: NodeJS.Timeout): void {
this.runningTimers.push(timerID);
}
/**
* Ends a timer.
*
* @param timerID Timer ID.
*/
public endTimer(timerID: NodeJS.Timeout): void {
const index = this.runningTimers.indexOf(timerID);
if (index !== -1) {
this.runningTimers.splice(index, 1);
}
if (!Object.keys(this.runningTasks).length && !this.runningTimers.length) {
this.cancelAll();
}
}
/**
* Starts an async task.
*
* @param abortHandler Abort handler.
* @returns Task ID.
*/
public startTask(abortHandler?: () => void): number {
const taskID = this.newTaskID();
this.runningTasks[taskID] = abortHandler ? abortHandler : () => {};
return taskID;
}
/**
* Ends an async task.
*
* @param taskID Task ID.
*/
public endTask(taskID: number): void {
if (this.runningTasks[taskID]) {
delete this.runningTasks[taskID];
if (!Object.keys(this.runningTasks).length && !this.runningTimers.length) {
this.cancelAll();
}
}
}
/**
* Returns the amount of running tasks.
*
* @returns Count.
*/
public getTaskCount(): number {
return Object.keys(this.runningTasks).length;
}
/**
* Returns a new task ID.
*
* @returns Task ID.
*/
private newTaskID(): number {
(<typeof AsyncTaskManager>this.constructor).taskID++;
return (<typeof AsyncTaskManager>this.constructor).taskID;
}
}