-
Notifications
You must be signed in to change notification settings - Fork 339
/
Copy pathtaskhandler.ts
130 lines (117 loc) · 3.16 KB
/
taskhandler.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
127
128
129
130
import { LinkedList } from '@lumino/collections';
import { UUID } from '@lumino/coreutils';
import { IDisposable } from '@lumino/disposable';
import { ISignal, Signal } from '@lumino/signaling';
/**
* A generic task handler
*/
export class TaskHandler<T> implements IDisposable {
constructor(model: T) {
this._taskChanged = new Signal<T, string>(model);
}
/**
* Boolean indicating whether the handler has been disposed.
*/
get isDisposed(): boolean {
return this._isDisposed;
}
/**
* Signal emitted when a task starts
*
* 'empty' is emitted each time the task list have processed all tasks
*/
get taskChanged(): ISignal<T, string> {
return this._taskChanged;
}
/**
* Adds a task to the list of pending model tasks.
*
* #Note:
* This will add a task name in the queue but the task
* execution remains in the hand of the caller.
* In particular it is the responsibility of the caller
* to call `remove(taskID)` when the task is executed.
*
* @param task - task name
* @returns task identifier
*/
add(task: string): string {
// Generate a unique task identifier:
const id = this._generateTaskID();
// Add the task to our list of pending tasks:
this._taskList.addLast({
id: id,
task: task
});
// If this task is the only task, broadcast the task...
if (this._taskList.length === 1) {
this._taskChanged.emit(task);
}
// Return the task identifier to allow consumers to remove the task once completed:
return id;
}
/**
* Dispose of task handler.
*/
dispose(): void {
if (this.isDisposed) {
return;
}
this._isDisposed = true;
Signal.clearData(this);
}
/**
* Add a asynchronous task to the stack and execute it
*
* @param name Name of the task
* @param callable Asynchronous task to be executed
*
* @returns The result of the task
*/
async execute<R>(name: string, callable: () => Promise<R>): Promise<R> {
const taskID = this.add(name);
try {
return await callable();
} finally {
this.remove(taskID);
}
}
/**
* Removes a task from the list of pending model tasks.
*
* @param id - task identifier
*/
remove(taskID: string): void {
let node = this._taskList.firstNode;
// Check the first node...
if (node?.value.id === taskID) {
this._taskList.removeNode(node);
} else {
// Walk the task list looking for a task with the provided identifier...
while (node?.next) {
node = node?.next;
if (node.value && node.value.id === taskID) {
this._taskList.removeNode(node);
break;
}
}
}
// Check for pending tasks and broadcast the oldest pending task...
if (this._taskList.length === 0) {
this._taskChanged.emit('empty');
} else {
this._taskChanged.emit(this._taskList.first.task);
}
}
/**
* Generates a unique task identifier.
*
* @returns task identifier
*/
private _generateTaskID(): string {
return UUID.uuid4();
}
private _isDisposed = false;
private _taskChanged: Signal<T, string>;
private _taskList: LinkedList<any> = new LinkedList();
}