UNPKG

7.3 kBJavaScriptView Raw
1"use strict";
2Object.defineProperty(exports, "__esModule", { value: true });
3const QueuePriority_1 = require("./QueuePriority");
4const events_1 = require("events");
5const uuid_1 = require("uuid");
6const OperationEvent_1 = require("./OperationEvent");
7const utils_1 = require("./utils");
8/**
9 * @class Operation
10 * @constructor Operation
11 * @description Abstract class which runs a single task
12 */
13class Operation extends events_1.EventEmitter {
14 /**
15 * @param {number} [id]
16 */
17 constructor(id = null) {
18 super();
19 this._queuePriority = QueuePriority_1.QueuePriority.normal;
20 if (!id) {
21 id = uuid_1.v4();
22 }
23 this.id = id;
24 this.name = null;
25 this.completionCallback = null;
26 this.map = {};
27 this.isExecuting = false;
28 this.error = true;
29 this.promise = null;
30 this.runPromise = null;
31 this._dependencies = [];
32 this._done = false;
33 this._isInQueue = false;
34 this._canStart = false;
35 this._cancelled = false;
36 this._resolve = null;
37 this._reject = null;
38 }
39 /**
40 * @description Getter returning if Operation finished its task
41 * @returns {boolean}
42 */
43 get isFinished() {
44 return this._done;
45 }
46 /**
47 * @description Knowing if operation was cancelled
48 * @returns {boolean}
49 */
50 get isCancelled() {
51 return this._cancelled;
52 }
53 /**
54 * Setter for isInQueue value
55 * @param {boolean} value
56 */
57 set isInQueue(value) {
58 this._isInQueue = value;
59 }
60 /**
61 * Getter for isInQueue value
62 */
63 get isInQueue() {
64 return this._isInQueue;
65 }
66 /**
67 * Setter for queuePriority value.
68 * It is only settable when operation has not yet executed, cancelled or finished
69 * @param {QueuePriority} value
70 */
71 set queuePriority(value) {
72 if (this.isExecuting || this.isCancelled || this.isFinished) {
73 return;
74 }
75 if (value in QueuePriority_1.QueuePriority) {
76 this._queuePriority = value;
77 }
78 }
79 /**
80 * Getter for queuePriority value
81 * @returns {QueuePriority|number}
82 */
83 get queuePriority() {
84 return this._queuePriority;
85 }
86 /**
87 * Setter for dependencies value.
88 * It is only settable when operation has not yet executed, cancelled or finished
89 * @param {Array.<Operation>} operations
90 */
91 set dependencies(operations) {
92 if (this.isExecuting || this.isCancelled || this.isFinished) {
93 return;
94 }
95 this._dependencies = operations;
96 }
97 /**
98 * Getter for dependencies value.
99 * When operation is executing, cancelled, or finished, this returns a copy
100 * @returns {Array.<Operation>}
101 */
102 get dependencies() {
103 if (this.isExecuting || this.isCancelled || this.isFinished) {
104 return utils_1.copyArray(this._dependencies);
105 }
106 return this._dependencies;
107 }
108 /**
109 * @description Cancels the operation if the operation has not started.
110 * @returns {undefined}
111 */
112 cancel() {
113 this._cancelled = true;
114 Promise.resolve(this.promise);
115 this.emit(OperationEvent_1.OperationEvent.CANCEL, this);
116 this._resolve && this._resolve();
117 }
118 /**
119 * @description Sets the operation as done. This is usually called internally or by the operationQueue
120 * @returns {undefined}
121 */
122 done() {
123 this._done = true;
124 this.completionCallback && this.completionCallback(this);
125 this.emit(OperationEvent_1.OperationEvent.DONE, this);
126 this._resolve && this._resolve(this.result);
127 }
128 /**
129 * @description Knowing if operation finished its task
130 * @returns {boolean}
131 */
132 isDone() {
133 return this._done;
134 }
135 /**
136 * Adds an operation as a dependency
137 * @param {Operation} dependency
138 */
139 addDependency(dependency) {
140 this._dependencies.push(dependency);
141 }
142 /**
143 * Removes an operation as a dependency
144 * @param {Operation} dependency
145 */
146 removeDependency(dependency) {
147 this._dependencies = this._dependencies.filter(operation => operation.id !== dependency.id);
148 }
149 /**
150 * Method to call to execute the operation's task.
151 * The operation will not run immediatly. It is evaluated at the next tick and evaluates it's depedencies
152 * in order to run. Calling this method multiples times will simply re-evaluate it's readiness
153 * @returns {Promise}
154 */
155 start() {
156 if (this.isExecuting || this.isCancelled || this.isFinished) {
157 return this.promise;
158 }
159 else if (this.promise && !this._canStart) {
160 this._preProcessStart();
161 }
162 else if (this.promise && this._canStart) {
163 if (this._isInQueue) {
164 this.emit(OperationEvent_1.OperationEvent.READY, this);
165 }
166 else {
167 this.main();
168 }
169 }
170 else {
171 this.promise = new Promise((resolve, reject) => {
172 this._resolve = resolve;
173 this._reject = reject;
174 this._preProcessStart();
175 });
176 }
177 return this.promise;
178 }
179 /**
180 */
181 main() {
182 this.isExecuting = true;
183 this.emit(OperationEvent_1.OperationEvent.START, this);
184 this.runPromise = this.run()
185 .then(result => {
186 this.result = result;
187 this.done();
188 })
189 .catch(e => {
190 this.isExecuting = false;
191 this.error = true;
192 this.emit(OperationEvent_1.OperationEvent.ERROR, { err: e, operation: this });
193 this.emit(OperationEvent_1.OperationEvent.DONE, this);
194 this._reject && this._reject();
195 });
196 }
197 _preProcessStart() {
198 this._createMap();
199 if (this._canStart) {
200 if (this._isInQueue) {
201 this.emit(OperationEvent_1.OperationEvent.READY, this);
202 }
203 else {
204 this.main();
205 }
206 }
207 }
208 _createMap() {
209 if (!this._dependencies.length) {
210 this._canStart = true;
211 return;
212 }
213 this._dependencies.forEach(operation => {
214 this.map[operation.id] = true;
215 operation.on(OperationEvent_1.OperationEvent.DONE, this._onDependantOperationDone.bind(this));
216 operation.start();
217 });
218 }
219 _onDependantOperationDone(operation) {
220 delete this.map[operation.id];
221 this._tryStart();
222 }
223 _tryStart() {
224 if (this.isExecuting || this.isCancelled || this.isFinished) {
225 return;
226 }
227 if (utils_1.isObjectEmpty(this.map)) {
228 // should emit event to let operation queue that this operation can start
229 // then it could check if maximum concurrent is not passed
230 this._canStart = true;
231 if (this.isInQueue) {
232 this.emit(OperationEvent_1.OperationEvent.READY, this);
233 }
234 else {
235 this.start();
236 }
237 }
238 }
239}
240exports.Operation = Operation;
241//# sourceMappingURL=Operation.js.map
\No newline at end of file