@guanghechen/task 1.0.0-alpha.9 → 1.0.0-beta.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +130 -25
- package/lib/cjs/index.cjs +229 -205
- package/lib/esm/index.mjs +227 -196
- package/lib/types/index.d.ts +95 -33
- package/package.json +15 -29
package/lib/esm/index.mjs
CHANGED
|
@@ -1,138 +1,136 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
import { SoraErrorCollector } from '@guanghechen/error';
|
|
4
|
-
import { Monitor } from '@guanghechen/monitor';
|
|
1
|
+
import { Observable } from '@guanghechen/observable';
|
|
2
|
+
import { ErrorLevelEnum } from '@guanghechen/error.types';
|
|
5
3
|
|
|
6
|
-
|
|
4
|
+
var TaskStrategyEnum;
|
|
5
|
+
(function (TaskStrategyEnum) {
|
|
6
|
+
TaskStrategyEnum[TaskStrategyEnum["ABORT_ON_ERROR"] = 1] = "ABORT_ON_ERROR";
|
|
7
|
+
TaskStrategyEnum[TaskStrategyEnum["CONTINUE_ON_ERROR"] = 2] = "CONTINUE_ON_ERROR";
|
|
8
|
+
})(TaskStrategyEnum || (TaskStrategyEnum = {}));
|
|
9
|
+
var TaskStatusEnum;
|
|
10
|
+
(function (TaskStatusEnum) {
|
|
11
|
+
TaskStatusEnum[TaskStatusEnum["PENDING"] = 1] = "PENDING";
|
|
12
|
+
TaskStatusEnum[TaskStatusEnum["RUNNING"] = 2] = "RUNNING";
|
|
13
|
+
TaskStatusEnum[TaskStatusEnum["SUSPENDED"] = 4] = "SUSPENDED";
|
|
14
|
+
TaskStatusEnum[TaskStatusEnum["CANCELLED"] = 8] = "CANCELLED";
|
|
15
|
+
TaskStatusEnum[TaskStatusEnum["FAILED"] = 16] = "FAILED";
|
|
16
|
+
TaskStatusEnum[TaskStatusEnum["COMPLETED"] = 32] = "COMPLETED";
|
|
17
|
+
TaskStatusEnum[TaskStatusEnum["ATTEMPT_SUSPENDING"] = 64] = "ATTEMPT_SUSPENDING";
|
|
18
|
+
TaskStatusEnum[TaskStatusEnum["ATTEMPT_RESUMING"] = 128] = "ATTEMPT_RESUMING";
|
|
19
|
+
TaskStatusEnum[TaskStatusEnum["ATTEMPT_CANCELING"] = 256] = "ATTEMPT_CANCELING";
|
|
20
|
+
TaskStatusEnum[TaskStatusEnum["ATTEMPT_COMPLETING"] = 512] = "ATTEMPT_COMPLETING";
|
|
21
|
+
})(TaskStatusEnum || (TaskStatusEnum = {}));
|
|
7
22
|
|
|
8
|
-
const
|
|
9
|
-
|
|
10
|
-
TaskStatusEnum.
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
TaskStatusEnum.
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
23
|
+
const _terminated = TaskStatusEnum.CANCELLED | TaskStatusEnum.FAILED | TaskStatusEnum.COMPLETED;
|
|
24
|
+
const _transitionMap = {
|
|
25
|
+
[TaskStatusEnum.PENDING]: TaskStatusEnum.PENDING |
|
|
26
|
+
TaskStatusEnum.RUNNING |
|
|
27
|
+
TaskStatusEnum.CANCELLED |
|
|
28
|
+
TaskStatusEnum.ATTEMPT_CANCELING,
|
|
29
|
+
[TaskStatusEnum.RUNNING]: TaskStatusEnum.RUNNING |
|
|
30
|
+
TaskStatusEnum.SUSPENDED |
|
|
31
|
+
_terminated |
|
|
32
|
+
TaskStatusEnum.ATTEMPT_SUSPENDING |
|
|
33
|
+
TaskStatusEnum.ATTEMPT_CANCELING |
|
|
34
|
+
TaskStatusEnum.ATTEMPT_COMPLETING,
|
|
35
|
+
[TaskStatusEnum.SUSPENDED]: TaskStatusEnum.RUNNING |
|
|
36
|
+
TaskStatusEnum.SUSPENDED |
|
|
37
|
+
_terminated |
|
|
38
|
+
TaskStatusEnum.ATTEMPT_RESUMING |
|
|
39
|
+
TaskStatusEnum.ATTEMPT_CANCELING |
|
|
40
|
+
TaskStatusEnum.ATTEMPT_COMPLETING,
|
|
41
|
+
[TaskStatusEnum.CANCELLED]: TaskStatusEnum.CANCELLED,
|
|
42
|
+
[TaskStatusEnum.FAILED]: TaskStatusEnum.FAILED,
|
|
43
|
+
[TaskStatusEnum.COMPLETED]: TaskStatusEnum.COMPLETED,
|
|
44
|
+
[TaskStatusEnum.ATTEMPT_SUSPENDING]: TaskStatusEnum.SUSPENDED |
|
|
45
|
+
_terminated |
|
|
46
|
+
TaskStatusEnum.ATTEMPT_SUSPENDING |
|
|
47
|
+
TaskStatusEnum.ATTEMPT_CANCELING |
|
|
48
|
+
TaskStatusEnum.ATTEMPT_COMPLETING,
|
|
49
|
+
[TaskStatusEnum.ATTEMPT_RESUMING]: TaskStatusEnum.RUNNING |
|
|
50
|
+
_terminated |
|
|
51
|
+
TaskStatusEnum.ATTEMPT_RESUMING |
|
|
52
|
+
TaskStatusEnum.ATTEMPT_CANCELING |
|
|
53
|
+
TaskStatusEnum.ATTEMPT_COMPLETING,
|
|
54
|
+
[TaskStatusEnum.ATTEMPT_CANCELING]: _terminated | TaskStatusEnum.ATTEMPT_CANCELING,
|
|
55
|
+
[TaskStatusEnum.ATTEMPT_COMPLETING]: _terminated | TaskStatusEnum.ATTEMPT_COMPLETING,
|
|
56
|
+
};
|
|
57
|
+
class TaskStatus extends Observable {
|
|
58
|
+
constructor() {
|
|
59
|
+
super(TaskStatusEnum.PENDING);
|
|
37
60
|
}
|
|
38
61
|
get alive() {
|
|
39
|
-
|
|
62
|
+
const value = this.getSnapshot();
|
|
63
|
+
return (value & _terminated) === 0;
|
|
40
64
|
}
|
|
41
65
|
get terminated() {
|
|
42
|
-
|
|
66
|
+
const value = this.getSnapshot();
|
|
67
|
+
return (value & _terminated) > 0;
|
|
43
68
|
}
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
return undefined;
|
|
50
|
-
return { from: this.name, details: this._errorCollector.errors };
|
|
51
|
-
}
|
|
52
|
-
set status(status) {
|
|
53
|
-
const curStatus = this._status;
|
|
54
|
-
if (status !== curStatus) {
|
|
55
|
-
const accepted = this.check(status);
|
|
56
|
-
if (accepted) {
|
|
57
|
-
this._status = status;
|
|
58
|
-
this._monitorStatusChange.notify(status, curStatus);
|
|
59
|
-
}
|
|
60
|
-
else {
|
|
61
|
-
throw new TypeError(`[transit] unexpected status: task(${this.name}) cur(${curStatus}) next(${status})`);
|
|
62
|
-
}
|
|
69
|
+
dispose() {
|
|
70
|
+
if (this.terminated) {
|
|
71
|
+
if (!this.disposed)
|
|
72
|
+
super.dispose();
|
|
73
|
+
return;
|
|
63
74
|
}
|
|
75
|
+
this.next(TaskStatusEnum.CANCELLED, { strict: false });
|
|
76
|
+
super.dispose();
|
|
64
77
|
}
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
return false;
|
|
81
|
-
case TaskStatusEnum.RUNNING:
|
|
82
|
-
return status === TaskStatusEnum.PENDING || status === TaskStatusEnum.ATTEMPT_RESUMING;
|
|
83
|
-
case TaskStatusEnum.SUSPENDED:
|
|
84
|
-
return status === TaskStatusEnum.ATTEMPT_SUSPENDING;
|
|
85
|
-
case TaskStatusEnum.CANCELLED:
|
|
86
|
-
return status === TaskStatusEnum.ATTEMPT_CANCELING;
|
|
87
|
-
case TaskStatusEnum.FAILED:
|
|
88
|
-
return (status !== TaskStatusEnum.PENDING &&
|
|
89
|
-
status !== TaskStatusEnum.SUSPENDED &&
|
|
90
|
-
(status & terminated) === 0);
|
|
91
|
-
case TaskStatusEnum.FINISHED:
|
|
92
|
-
return (status !== TaskStatusEnum.PENDING &&
|
|
93
|
-
status !== TaskStatusEnum.SUSPENDED &&
|
|
94
|
-
(status & terminated) === 0);
|
|
95
|
-
case TaskStatusEnum.ATTEMPT_SUSPENDING:
|
|
96
|
-
return status === TaskStatusEnum.RUNNING;
|
|
97
|
-
case TaskStatusEnum.ATTEMPT_RESUMING:
|
|
98
|
-
return status === TaskStatusEnum.SUSPENDED;
|
|
99
|
-
case TaskStatusEnum.ATTEMPT_CANCELING:
|
|
100
|
-
return (status & alive) > 0;
|
|
101
|
-
case TaskStatusEnum.ATTEMPT_FINISHING:
|
|
102
|
-
return status !== TaskStatusEnum.PENDING && (status & alive) > 0;
|
|
103
|
-
default:
|
|
104
|
-
return false;
|
|
78
|
+
next(nextStatus, options) {
|
|
79
|
+
const curStatus = this.getSnapshot();
|
|
80
|
+
if (curStatus === nextStatus)
|
|
81
|
+
return;
|
|
82
|
+
if (this._verifyTransition(curStatus, nextStatus)) {
|
|
83
|
+
super.next(nextStatus, options);
|
|
84
|
+
if ((nextStatus & _terminated) > 0)
|
|
85
|
+
this.dispose();
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
const strict = options?.strict ?? true;
|
|
89
|
+
if (strict) {
|
|
90
|
+
const curStatusName = TaskStatusEnum[curStatus];
|
|
91
|
+
const nextStatusName = TaskStatusEnum[nextStatus];
|
|
92
|
+
throw new RangeError(`Invalid status transition: ${curStatusName} -> ${nextStatusName}.`);
|
|
105
93
|
}
|
|
106
94
|
}
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
this._monitorAddError.destroy();
|
|
113
|
-
}
|
|
114
|
-
_addError(type, error, level = ErrorLevelEnum.ERROR) {
|
|
115
|
-
this._errorCollector.add(type, error, level);
|
|
116
|
-
this._monitorAddError.notify(type, error, level);
|
|
95
|
+
_verifyTransition(curStatus, nextStatus) {
|
|
96
|
+
const validTransitions = _transitionMap[curStatus];
|
|
97
|
+
if (validTransitions === undefined)
|
|
98
|
+
return false;
|
|
99
|
+
return (nextStatus & validTransitions) > 0;
|
|
117
100
|
}
|
|
118
101
|
}
|
|
119
102
|
|
|
120
|
-
class AtomicTask
|
|
103
|
+
class AtomicTask {
|
|
104
|
+
name;
|
|
105
|
+
status;
|
|
106
|
+
strategy;
|
|
107
|
+
_errors;
|
|
121
108
|
_promise;
|
|
122
|
-
constructor(name) {
|
|
123
|
-
|
|
109
|
+
constructor(name, strategy) {
|
|
110
|
+
this.name = name;
|
|
111
|
+
this.strategy = strategy;
|
|
112
|
+
this.status = new TaskStatus();
|
|
113
|
+
this._errors = [];
|
|
124
114
|
this._promise = undefined;
|
|
125
115
|
}
|
|
116
|
+
get errors() {
|
|
117
|
+
return this._errors;
|
|
118
|
+
}
|
|
126
119
|
async start() {
|
|
127
|
-
if (this.status === TaskStatusEnum.PENDING) {
|
|
128
|
-
this.status
|
|
120
|
+
if (this.status.getSnapshot() === TaskStatusEnum.PENDING) {
|
|
121
|
+
this.status.next(TaskStatusEnum.RUNNING);
|
|
129
122
|
this._promise = this.run()
|
|
130
123
|
.then(() => {
|
|
131
|
-
this.status
|
|
124
|
+
this.status.next(TaskStatusEnum.COMPLETED, { strict: false });
|
|
132
125
|
})
|
|
133
126
|
.catch(error => {
|
|
134
|
-
|
|
135
|
-
|
|
127
|
+
const soraError = {
|
|
128
|
+
from: this.name,
|
|
129
|
+
level: ErrorLevelEnum.ERROR,
|
|
130
|
+
details: error,
|
|
131
|
+
};
|
|
132
|
+
this._errors.push(soraError);
|
|
133
|
+
this.status.next(TaskStatusEnum.FAILED, { strict: false });
|
|
136
134
|
});
|
|
137
135
|
}
|
|
138
136
|
return this._promise;
|
|
@@ -144,131 +142,164 @@ class AtomicTask extends TaskState {
|
|
|
144
142
|
await this._promise;
|
|
145
143
|
}
|
|
146
144
|
async cancel() {
|
|
147
|
-
if (this.status
|
|
148
|
-
this.status = TaskStatusEnum.ATTEMPT_CANCELING;
|
|
149
|
-
this.status = TaskStatusEnum.CANCELLED;
|
|
145
|
+
if (this.status.terminated)
|
|
150
146
|
return;
|
|
151
|
-
|
|
152
|
-
if (this.alive)
|
|
153
|
-
this.status = TaskStatusEnum.ATTEMPT_CANCELING;
|
|
147
|
+
this.status.next(TaskStatusEnum.ATTEMPT_CANCELING);
|
|
154
148
|
await this._promise;
|
|
149
|
+
this.status.next(TaskStatusEnum.CANCELLED, { strict: false });
|
|
155
150
|
}
|
|
156
|
-
async
|
|
157
|
-
|
|
151
|
+
async complete() {
|
|
152
|
+
const status = this.status;
|
|
153
|
+
if (status.getSnapshot() === TaskStatusEnum.PENDING)
|
|
158
154
|
await this.start();
|
|
159
|
-
if (
|
|
160
|
-
|
|
155
|
+
if (status.terminated)
|
|
156
|
+
return;
|
|
157
|
+
status.next(TaskStatusEnum.ATTEMPT_COMPLETING);
|
|
161
158
|
await this._promise;
|
|
159
|
+
status.next(TaskStatusEnum.COMPLETED, { strict: false });
|
|
162
160
|
}
|
|
163
161
|
}
|
|
164
162
|
|
|
165
|
-
class ResumableTask
|
|
163
|
+
class ResumableTask {
|
|
164
|
+
name;
|
|
165
|
+
status;
|
|
166
166
|
strategy;
|
|
167
|
+
_errors;
|
|
167
168
|
_pollInterval;
|
|
168
169
|
_execution;
|
|
169
170
|
_step;
|
|
170
|
-
constructor(
|
|
171
|
-
|
|
172
|
-
this.strategy =
|
|
173
|
-
this.
|
|
171
|
+
constructor(name, strategy, pollInterval) {
|
|
172
|
+
this.name = name;
|
|
173
|
+
this.strategy = strategy;
|
|
174
|
+
this.status = new TaskStatus();
|
|
175
|
+
this._errors = [];
|
|
176
|
+
this._pollInterval = Math.max(0, pollInterval);
|
|
174
177
|
this._execution = undefined;
|
|
175
178
|
this._step = undefined;
|
|
176
179
|
}
|
|
180
|
+
get errors() {
|
|
181
|
+
return this._errors;
|
|
182
|
+
}
|
|
177
183
|
async start() {
|
|
178
|
-
if (this.status === TaskStatusEnum.PENDING) {
|
|
179
|
-
this.status
|
|
184
|
+
if (this.status.getSnapshot() === TaskStatusEnum.PENDING) {
|
|
185
|
+
this.status.next(TaskStatusEnum.RUNNING);
|
|
180
186
|
this._execution = this.run();
|
|
181
|
-
this.
|
|
182
|
-
await this._step;
|
|
187
|
+
void this._launchStep();
|
|
183
188
|
}
|
|
184
189
|
}
|
|
185
190
|
async pause() {
|
|
186
|
-
if (this.status === TaskStatusEnum.RUNNING) {
|
|
187
|
-
this.status
|
|
191
|
+
if (this.status.getSnapshot() === TaskStatusEnum.RUNNING) {
|
|
192
|
+
this.status.next(TaskStatusEnum.ATTEMPT_SUSPENDING);
|
|
188
193
|
await this._step;
|
|
189
|
-
|
|
190
|
-
this.status = TaskStatusEnum.SUSPENDED;
|
|
191
|
-
}
|
|
194
|
+
this.status.next(TaskStatusEnum.SUSPENDED, { strict: false });
|
|
192
195
|
}
|
|
193
196
|
}
|
|
194
197
|
async resume() {
|
|
195
|
-
if (this.status === TaskStatusEnum.SUSPENDED) {
|
|
196
|
-
this.status
|
|
198
|
+
if (this.status.getSnapshot() === TaskStatusEnum.SUSPENDED) {
|
|
199
|
+
this.status.next(TaskStatusEnum.ATTEMPT_RESUMING);
|
|
197
200
|
await this._step;
|
|
198
|
-
if (this.status === TaskStatusEnum.ATTEMPT_RESUMING) {
|
|
199
|
-
this.status
|
|
200
|
-
this.
|
|
201
|
+
if (this.status.getSnapshot() === TaskStatusEnum.ATTEMPT_RESUMING) {
|
|
202
|
+
this.status.next(TaskStatusEnum.RUNNING);
|
|
203
|
+
void this._queueStep();
|
|
201
204
|
}
|
|
202
205
|
}
|
|
203
206
|
}
|
|
204
207
|
async cancel() {
|
|
205
|
-
if (this.
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
}
|
|
211
|
-
}
|
|
208
|
+
if (this.status.terminated)
|
|
209
|
+
return;
|
|
210
|
+
this.status.next(TaskStatusEnum.ATTEMPT_CANCELING);
|
|
211
|
+
await this._step;
|
|
212
|
+
this.status.next(TaskStatusEnum.CANCELLED, { strict: false });
|
|
212
213
|
}
|
|
213
|
-
async
|
|
214
|
-
|
|
214
|
+
async complete() {
|
|
215
|
+
const status = this.status;
|
|
216
|
+
if (status.getSnapshot() === TaskStatusEnum.PENDING)
|
|
215
217
|
await this.start();
|
|
216
|
-
if (
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
if (step.done) {
|
|
224
|
-
this.status = this.hasError ? TaskStatusEnum.FAILED : TaskStatusEnum.FINISHED;
|
|
225
|
-
break;
|
|
226
|
-
}
|
|
227
|
-
await step.value.catch(error => {
|
|
228
|
-
this._addError('ResumableTaskError', error);
|
|
229
|
-
switch (this.strategy) {
|
|
230
|
-
case TaskStrategyEnum.ABORT_ON_ERROR:
|
|
231
|
-
if (!this.terminated)
|
|
232
|
-
this.status = TaskStatusEnum.FAILED;
|
|
233
|
-
break;
|
|
234
|
-
case TaskStrategyEnum.CONTINUE_ON_ERROR:
|
|
235
|
-
break;
|
|
236
|
-
}
|
|
237
|
-
});
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
}
|
|
242
|
-
launchStep() {
|
|
243
|
-
if (this.status === TaskStatusEnum.RUNNING && this._step === undefined && this._execution) {
|
|
244
|
-
const step = this._execution.next();
|
|
218
|
+
if (status.terminated)
|
|
219
|
+
return;
|
|
220
|
+
status.next(TaskStatusEnum.ATTEMPT_COMPLETING);
|
|
221
|
+
await this._step;
|
|
222
|
+
const execution = this._execution;
|
|
223
|
+
for (let alive = true; alive;) {
|
|
224
|
+
const step = execution.next();
|
|
245
225
|
if (step.done) {
|
|
246
|
-
|
|
247
|
-
|
|
226
|
+
const nextStatus = this._errors.length > 0 ? TaskStatusEnum.FAILED : TaskStatusEnum.COMPLETED;
|
|
227
|
+
status.next(nextStatus, { strict: false });
|
|
228
|
+
break;
|
|
248
229
|
}
|
|
249
|
-
|
|
250
|
-
.
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
230
|
+
try {
|
|
231
|
+
await step.value;
|
|
232
|
+
}
|
|
233
|
+
catch (error) {
|
|
234
|
+
const soraError = {
|
|
235
|
+
from: this.name,
|
|
236
|
+
level: ErrorLevelEnum.ERROR,
|
|
237
|
+
details: error,
|
|
238
|
+
};
|
|
239
|
+
this._errors.push(soraError);
|
|
257
240
|
switch (this.strategy) {
|
|
258
241
|
case TaskStrategyEnum.ABORT_ON_ERROR:
|
|
259
|
-
|
|
260
|
-
|
|
242
|
+
status.next(TaskStatusEnum.FAILED, { strict: false });
|
|
243
|
+
alive = false;
|
|
261
244
|
break;
|
|
262
245
|
case TaskStrategyEnum.CONTINUE_ON_ERROR:
|
|
263
|
-
this.
|
|
246
|
+
await this._queueStep();
|
|
264
247
|
break;
|
|
265
248
|
}
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
if (status.alive) {
|
|
252
|
+
const soraError = {
|
|
253
|
+
from: this.name,
|
|
254
|
+
level: ErrorLevelEnum.ERROR,
|
|
255
|
+
details: new RangeError('The task is not terminated.'),
|
|
256
|
+
};
|
|
257
|
+
this._errors.push(soraError);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
async _launchStep() {
|
|
261
|
+
if (this._execution === undefined)
|
|
262
|
+
return;
|
|
263
|
+
if (this._step !== undefined)
|
|
264
|
+
return;
|
|
265
|
+
if (this.status.getSnapshot() !== TaskStatusEnum.RUNNING)
|
|
266
|
+
return;
|
|
267
|
+
const execution = this._execution;
|
|
268
|
+
const step = execution.next();
|
|
269
|
+
if (step.done) {
|
|
270
|
+
this.status.next(this._errors.length > 0 ? TaskStatusEnum.FAILED : TaskStatusEnum.COMPLETED, {
|
|
271
|
+
strict: false,
|
|
266
272
|
});
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
this._step = step.value;
|
|
276
|
+
try {
|
|
277
|
+
await step.value;
|
|
278
|
+
this._step = undefined;
|
|
279
|
+
void this._queueStep();
|
|
280
|
+
}
|
|
281
|
+
catch (error) {
|
|
282
|
+
this._step = undefined;
|
|
283
|
+
const soraError = {
|
|
284
|
+
from: this.name,
|
|
285
|
+
level: ErrorLevelEnum.ERROR,
|
|
286
|
+
details: error,
|
|
287
|
+
};
|
|
288
|
+
this._errors.push(soraError);
|
|
289
|
+
switch (this.strategy) {
|
|
290
|
+
case TaskStrategyEnum.ABORT_ON_ERROR:
|
|
291
|
+
this.status.next(TaskStatusEnum.FAILED, { strict: false });
|
|
292
|
+
break;
|
|
293
|
+
case TaskStrategyEnum.CONTINUE_ON_ERROR:
|
|
294
|
+
void this._queueStep();
|
|
295
|
+
break;
|
|
296
|
+
}
|
|
267
297
|
}
|
|
268
298
|
}
|
|
269
|
-
|
|
270
|
-
|
|
299
|
+
async _queueStep() {
|
|
300
|
+
await new Promise(resolve => setTimeout(resolve, this._pollInterval));
|
|
301
|
+
await this._launchStep();
|
|
271
302
|
}
|
|
272
303
|
}
|
|
273
304
|
|
|
274
|
-
export { AtomicTask, ResumableTask,
|
|
305
|
+
export { AtomicTask, ResumableTask, TaskStatus, TaskStatusEnum, TaskStrategyEnum };
|
package/lib/types/index.d.ts
CHANGED
|
@@ -1,58 +1,120 @@
|
|
|
1
|
-
import {
|
|
2
|
-
export { TaskStatusEnum, TaskStrategyEnum } from '@guanghechen/constant';
|
|
3
|
-
import { ITaskState, ISoraErrorCollector, ITaskError, ITaskMonitor, IUnMonitorTask, ITask } from '@guanghechen/types';
|
|
4
|
-
export { ITask, ITaskError, ITaskMonitor, ITaskState } from '@guanghechen/types';
|
|
1
|
+
import { IObservable, IObservableNextOptions, Observable } from '@guanghechen/observable';
|
|
5
2
|
|
|
6
|
-
declare
|
|
3
|
+
declare enum TaskStrategyEnum {
|
|
4
|
+
ABORT_ON_ERROR = 1,// Abort the task if any error occurred.
|
|
5
|
+
CONTINUE_ON_ERROR = 2
|
|
6
|
+
}
|
|
7
|
+
declare enum TaskStatusEnum {
|
|
8
|
+
PENDING = 1,// Task not start.
|
|
9
|
+
RUNNING = 2,// Task is running.
|
|
10
|
+
SUSPENDED = 4,// Task is paused.
|
|
11
|
+
CANCELLED = 8,// Task is cancelled.
|
|
12
|
+
FAILED = 16,// Task is failed.
|
|
13
|
+
COMPLETED = 32,// Task is completed.
|
|
14
|
+
ATTEMPT_SUSPENDING = 64,// Attempting to suspend the task.
|
|
15
|
+
ATTEMPT_RESUMING = 128,// Attempting to resume the task.
|
|
16
|
+
ATTEMPT_CANCELING = 256,// Attempting to cancel the task.
|
|
17
|
+
ATTEMPT_COMPLETING = 512
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface ITaskStatus extends IObservable<TaskStatusEnum> {
|
|
21
|
+
/**
|
|
22
|
+
* Whether the task was alive.
|
|
23
|
+
*/
|
|
24
|
+
readonly alive: boolean;
|
|
25
|
+
/**
|
|
26
|
+
* Whether the task was terminated.
|
|
27
|
+
*/
|
|
28
|
+
readonly terminated: boolean;
|
|
29
|
+
/**
|
|
30
|
+
*
|
|
31
|
+
* @param nextStatus
|
|
32
|
+
* @param options
|
|
33
|
+
*/
|
|
34
|
+
next(nextStatus: TaskStatusEnum, options?: IObservableNextOptions): void;
|
|
35
|
+
}
|
|
36
|
+
interface ITask {
|
|
37
|
+
/**
|
|
38
|
+
* Task name.
|
|
39
|
+
*/
|
|
7
40
|
readonly name: string;
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
41
|
+
/**
|
|
42
|
+
* Task status.
|
|
43
|
+
*/
|
|
44
|
+
readonly status: ITaskStatus;
|
|
45
|
+
/**
|
|
46
|
+
* Task strategy.
|
|
47
|
+
*/
|
|
48
|
+
readonly strategy: TaskStrategyEnum;
|
|
49
|
+
/**
|
|
50
|
+
* Errors while run the task.
|
|
51
|
+
*/
|
|
52
|
+
readonly errors: ReadonlyArray<unknown>;
|
|
53
|
+
/**
|
|
54
|
+
* Start the task.
|
|
55
|
+
*/
|
|
56
|
+
start(): Promise<void>;
|
|
57
|
+
/**
|
|
58
|
+
* Pause the task.
|
|
59
|
+
*/
|
|
60
|
+
pause(): Promise<void>;
|
|
61
|
+
/**
|
|
62
|
+
* Resume the task.
|
|
63
|
+
*/
|
|
64
|
+
resume(): Promise<void>;
|
|
65
|
+
/**
|
|
66
|
+
* Cancel the task.
|
|
67
|
+
*/
|
|
68
|
+
cancel(): Promise<void>;
|
|
69
|
+
/**
|
|
70
|
+
* finish the task: run to completed no matter if the task started or not.
|
|
71
|
+
*/
|
|
72
|
+
complete(): Promise<void>;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
declare class TaskStatus extends Observable<TaskStatusEnum> implements ITaskStatus {
|
|
76
|
+
constructor();
|
|
15
77
|
get alive(): boolean;
|
|
16
78
|
get terminated(): boolean;
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
monitor(monitor: Partial<ITaskMonitor>): IUnMonitorTask;
|
|
21
|
-
check(nextStatus: TaskStatusEnum): boolean;
|
|
22
|
-
cleanup(): void;
|
|
23
|
-
protected _addError(type: string, error: unknown, level?: ErrorLevelEnum): void;
|
|
79
|
+
dispose(): void;
|
|
80
|
+
next(nextStatus: TaskStatusEnum, options?: IObservableNextOptions): void;
|
|
81
|
+
protected _verifyTransition(curStatus: TaskStatusEnum, nextStatus: TaskStatusEnum): boolean;
|
|
24
82
|
}
|
|
25
83
|
|
|
26
|
-
declare abstract class AtomicTask
|
|
84
|
+
declare abstract class AtomicTask implements ITask {
|
|
85
|
+
readonly name: string;
|
|
86
|
+
readonly status: ITaskStatus;
|
|
87
|
+
readonly strategy: TaskStrategyEnum;
|
|
88
|
+
protected readonly _errors: unknown[];
|
|
27
89
|
private _promise;
|
|
28
|
-
constructor(name: string);
|
|
90
|
+
constructor(name: string, strategy: TaskStrategyEnum);
|
|
91
|
+
get errors(): ReadonlyArray<unknown>;
|
|
29
92
|
start(): Promise<void>;
|
|
30
93
|
pause(): Promise<void>;
|
|
31
94
|
resume(): Promise<void>;
|
|
32
95
|
cancel(): Promise<void>;
|
|
33
|
-
|
|
96
|
+
complete(): Promise<void>;
|
|
34
97
|
protected abstract run(): Promise<void>;
|
|
35
98
|
}
|
|
36
99
|
|
|
37
|
-
|
|
38
|
-
name: string;
|
|
39
|
-
|
|
40
|
-
pollInterval: number;
|
|
41
|
-
}
|
|
42
|
-
declare abstract class ResumableTask extends TaskState implements ITask {
|
|
100
|
+
declare abstract class ResumableTask implements ITask {
|
|
101
|
+
readonly name: string;
|
|
102
|
+
readonly status: ITaskStatus;
|
|
43
103
|
readonly strategy: TaskStrategyEnum;
|
|
104
|
+
protected readonly _errors: unknown[];
|
|
44
105
|
private readonly _pollInterval;
|
|
45
106
|
private _execution;
|
|
46
107
|
private _step;
|
|
47
|
-
constructor(
|
|
108
|
+
constructor(name: string, strategy: TaskStrategyEnum, pollInterval: number);
|
|
109
|
+
get errors(): ReadonlyArray<unknown>;
|
|
48
110
|
start(): Promise<void>;
|
|
49
111
|
pause(): Promise<void>;
|
|
50
112
|
resume(): Promise<void>;
|
|
51
113
|
cancel(): Promise<void>;
|
|
52
|
-
|
|
114
|
+
complete(): Promise<void>;
|
|
53
115
|
protected abstract run(): IterableIterator<Promise<void>>;
|
|
54
|
-
private
|
|
55
|
-
private
|
|
116
|
+
private _launchStep;
|
|
117
|
+
private _queueStep;
|
|
56
118
|
}
|
|
57
119
|
|
|
58
|
-
export { AtomicTask, ResumableTask,
|
|
120
|
+
export { AtomicTask, type ITask, type ITaskStatus, ResumableTask, TaskStatus, TaskStatusEnum, TaskStrategyEnum };
|