utiller 1.0.412 → 1.0.413
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/index.js +1 -21
- package/lib/exceptioner/ERRORs.js +1 -192
- package/lib/exceptioner/index.js +1 -39
- package/lib/index.js +1 -30
- package/lib/pooller/index.js +1 -1117
- package/lib/utiller/index.js +1 -3531
- package/lib/utiller/nodeutiller.js +3 -1095
- package/package.json +1 -1
- package/template/sample.package.json +1 -1
package/lib/pooller/index.js
CHANGED
|
@@ -1,1117 +1 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
|
|
3
|
-
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
-
Object.defineProperty(exports, "__esModule", {
|
|
5
|
-
value: true
|
|
6
|
-
});
|
|
7
|
-
exports.default = void 0;
|
|
8
|
-
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
9
|
-
var _lodash = _interopRequireDefault(require("lodash"));
|
|
10
|
-
var _index2 = require("../index.js");
|
|
11
|
-
var _configerer = require("configerer");
|
|
12
|
-
var _exceptioner = _interopRequireDefault(require("../exceptioner"));
|
|
13
|
-
function _classPrivateFieldInitSpec(e, t, a) { _checkPrivateRedeclaration(e, t), t.set(e, a); }
|
|
14
|
-
function _checkPrivateRedeclaration(e, t) { if (t.has(e)) throw new TypeError("Cannot initialize the same private elements twice on an object"); }
|
|
15
|
-
function _classPrivateFieldGet(s, a) { return s.get(_assertClassBrand(s, a)); }
|
|
16
|
-
function _assertClassBrand(e, t, n) { if ("function" == typeof e ? e === t : e.has(t)) return arguments.length < 3 ? t : n; throw new TypeError("Private element is not present on this object"); }
|
|
17
|
-
/**
|
|
18
|
-
* InfinitePool - 一個功能豐富的異步任務池管理器
|
|
19
|
-
*
|
|
20
|
-
* 核心特性:
|
|
21
|
-
* 1. 任務超時控制 - 防止任務執行時間過長
|
|
22
|
-
* 2. 任務間隔控制 - 控制任務執行頻率,適用於爬蟲等場景
|
|
23
|
-
* 3. 空閒等待機制 - 當無任務時可設定睡眠時間和次數
|
|
24
|
-
* 4. 任務取消機制 - 通過 hash 取消尚未執行的任務
|
|
25
|
-
* 5. 多種運行模式 - runByParams, runByTimes, runInInfinite, runByEachTask
|
|
26
|
-
* 6. 錯誤處理機制 - 可設定 taskFailHandler 避免錯誤中斷整個池
|
|
27
|
-
* 7. 優先級隊列 - 支持 high/medium/low 三種優先級
|
|
28
|
-
*/
|
|
29
|
-
const SPECIFICITY_DEBUG = false;
|
|
30
|
-
var _run = /*#__PURE__*/new WeakMap();
|
|
31
|
-
class InfinitePool {
|
|
32
|
-
/**
|
|
33
|
-
* 構造函數
|
|
34
|
-
* @param {number} maxWorkers - 最大 worker 數量(並發任務數)
|
|
35
|
-
* @param {string|number} name - 池的名稱或 ID
|
|
36
|
-
*/
|
|
37
|
-
constructor(maxWorkers = _configerer.configerer.POOLLER_WORKER_DEFAULT, name = _index2.utiller.getRandomValue(0, 100000000000)) {
|
|
38
|
-
/** 標記是否以背景模式運行 */
|
|
39
|
-
(0, _defineProperty2.default)(this, "isRunInBackgroundMode", false);
|
|
40
|
-
/** 當前運行狀態,默認為 RUN_BY_EACH_TASK 模式 */
|
|
41
|
-
(0, _defineProperty2.default)(this, "state", _configerer.configerer.POOLLER_STATE.RUN_BY_EACH_TASK);
|
|
42
|
-
/**
|
|
43
|
-
* 任務間隔睡眠機制
|
|
44
|
-
* 用於處理需要延遲的任務(如網頁爬蟲,需要模擬人工操作頻率)
|
|
45
|
-
* 當 worker 滿載後,新加入的任務會根據此設定延遲執行
|
|
46
|
-
*/
|
|
47
|
-
(0, _defineProperty2.default)(this, "enableOfTaskSleepByInterval", _configerer.configerer.POOLLER_ENABLE_TASK_SLEEP_BY_INTERVAL);
|
|
48
|
-
(0, _defineProperty2.default)(this, "taskSleepInterval", _configerer.configerer.POOLLER_TASK_OF_INTERVAL_DEFAULT);
|
|
49
|
-
/**
|
|
50
|
-
* 任務超時機制
|
|
51
|
-
* 避免單個任務執行時間過長阻塞整個隊列
|
|
52
|
-
*/
|
|
53
|
-
(0, _defineProperty2.default)(this, "enableOfTaskTimeout", _configerer.configerer.POOLLER_ENABLE_TIMEOUT);
|
|
54
|
-
(0, _defineProperty2.default)(this, "timeOfTaskTimeout", _configerer.configerer.POOLLER_TASK_TIMEOUT_DEFAULT);
|
|
55
|
-
/**
|
|
56
|
-
* 任務失敗處理器
|
|
57
|
-
* 如果不設置,任務失敗會導致整個池中斷
|
|
58
|
-
*/
|
|
59
|
-
(0, _defineProperty2.default)(this, "handlerOfAssignTaskFail", undefined);
|
|
60
|
-
/** Worker 最大數量(並發執行的任務數上限) */
|
|
61
|
-
(0, _defineProperty2.default)(this, "maximumOfWorker", void 0);
|
|
62
|
-
/**
|
|
63
|
-
* 是否禁用首次立即執行
|
|
64
|
-
* 如果設定 sleep interval,可通過此參數控制是否先執行第一次再開始 interval 機制
|
|
65
|
-
*/
|
|
66
|
-
(0, _defineProperty2.default)(this, "disableFirstRun", false);
|
|
67
|
-
/**
|
|
68
|
-
* 等待參數隊列
|
|
69
|
-
* 用於 runByParam 模式,存儲待處理的參數
|
|
70
|
-
*/
|
|
71
|
-
(0, _defineProperty2.default)(this, "queueOfWaitingParam", []);
|
|
72
|
-
/**
|
|
73
|
-
* runByTimes 模式的剩餘執行次數
|
|
74
|
-
* -1 表示未使用此模式
|
|
75
|
-
*/
|
|
76
|
-
(0, _defineProperty2.default)(this, "countsOfRunByTimes", -1);
|
|
77
|
-
/**
|
|
78
|
-
* 已分配任務隊列(按優先級分組)
|
|
79
|
-
* 結構: { high: [], medium: [], low: [] }
|
|
80
|
-
* 存儲尚未開始執行的任務
|
|
81
|
-
*/
|
|
82
|
-
(0, _defineProperty2.default)(this, "queueOfAssignTask", {});
|
|
83
|
-
/**
|
|
84
|
-
* 正在執行的任務隊列
|
|
85
|
-
* 存儲當前 worker 正在處理的任務(無法取消)
|
|
86
|
-
* 隊列大小不應超過 maximumOfWorker
|
|
87
|
-
*/
|
|
88
|
-
(0, _defineProperty2.default)(this, "queueOfExecutingTask", []);
|
|
89
|
-
/**
|
|
90
|
-
* 隊列輪詢狀態標記
|
|
91
|
-
* true 表示池正在運行,false 表示停止
|
|
92
|
-
*/
|
|
93
|
-
(0, _defineProperty2.default)(this, "isQueuePolling", false);
|
|
94
|
-
/**
|
|
95
|
-
* 初始任務完成標記
|
|
96
|
-
* 用於 disableFirstRun 機制
|
|
97
|
-
*/
|
|
98
|
-
(0, _defineProperty2.default)(this, "initialTaskCompleted", false);
|
|
99
|
-
/**
|
|
100
|
-
* Hash 到任務信息的映射
|
|
101
|
-
* 用於通過 hash 查找和刪除未執行的任務
|
|
102
|
-
* 注意:僅限 runByTask 模式,其他模式下任務執行後 hash 會改變
|
|
103
|
-
*/
|
|
104
|
-
(0, _defineProperty2.default)(this, "mapOfHashNTask", {});
|
|
105
|
-
/**
|
|
106
|
-
* Hash 到回調包裝器的映射
|
|
107
|
-
* 用於 addTaskAndWait4Result 模式,存儲等待結果的回調函數
|
|
108
|
-
*/
|
|
109
|
-
(0, _defineProperty2.default)(this, "mapOfHashNCallbackWrapper", {});
|
|
110
|
-
/** 當前池的名稱/ID */
|
|
111
|
-
(0, _defineProperty2.default)(this, "nameOfCurrentPool", ``);
|
|
112
|
-
/**
|
|
113
|
-
* 原子化的背景實例引用
|
|
114
|
-
* 用於防止重複創建背景任務
|
|
115
|
-
*/
|
|
116
|
-
(0, _defineProperty2.default)(this, "atomicBgInstance", undefined);
|
|
117
|
-
/**
|
|
118
|
-
* 設置池的 ID
|
|
119
|
-
* @param {string} id - 池的唯一標識符
|
|
120
|
-
*/
|
|
121
|
-
(0, _defineProperty2.default)(this, "setPoolId", (id = this.nameOfCurrentPool) => {
|
|
122
|
-
this.nameOfCurrentPool = id;
|
|
123
|
-
});
|
|
124
|
-
/**
|
|
125
|
-
* 獲取池的 ID
|
|
126
|
-
* @returns {string} 池的唯一標識符
|
|
127
|
-
*/
|
|
128
|
-
(0, _defineProperty2.default)(this, "getPoolId", () => {
|
|
129
|
-
return this.nameOfCurrentPool;
|
|
130
|
-
});
|
|
131
|
-
/**
|
|
132
|
-
* 在背景模式下停止池
|
|
133
|
-
* 等待所有執行中的任務完成,最多等待 15 秒
|
|
134
|
-
*
|
|
135
|
-
* @returns {Promise<boolean>} 返回 true 表示停止完成
|
|
136
|
-
*
|
|
137
|
-
* TODO: 應該設計成當 terminate 後,監聽 executingTaskInQueue 為零時才回傳結束
|
|
138
|
-
*
|
|
139
|
-
* [邏輯漏洞修復] 應該在超時後返回 false 表示強制終止,而不是返回 true
|
|
140
|
-
*/
|
|
141
|
-
(0, _defineProperty2.default)(this, "stopInBackground", async () => {
|
|
142
|
-
this.terminate();
|
|
143
|
-
let attempts = 0;
|
|
144
|
-
const maxAttempts = 30; // 30 * 500ms = 15 秒最大等待時間
|
|
145
|
-
while (_lodash.default.size(this.queueOfExecutingTask) > 0 && attempts < maxAttempts) {
|
|
146
|
-
await _index2.utiller.syncDelay(500);
|
|
147
|
-
this.printLogMessage(`784512, 卡在 stopInBackground 出不來,${this.getLogMessageOfExecutingTaskQueueCount()}`);
|
|
148
|
-
this.showState();
|
|
149
|
-
attempts++;
|
|
150
|
-
}
|
|
151
|
-
|
|
152
|
-
// [修復] 如果超時仍有任務在執行,應該返回 false
|
|
153
|
-
if (_lodash.default.size(this.queueOfExecutingTask) > 0) {
|
|
154
|
-
this.printLogMessage(`stopInBackground 超時,仍有 ${_lodash.default.size(this.queueOfExecutingTask)} 個任務在執行`, true);
|
|
155
|
-
return false;
|
|
156
|
-
}
|
|
157
|
-
return true;
|
|
158
|
-
});
|
|
159
|
-
/**
|
|
160
|
-
* 檢查池是否正在運行
|
|
161
|
-
* @returns {boolean} true 表示正在運行
|
|
162
|
-
*/
|
|
163
|
-
(0, _defineProperty2.default)(this, "isRunning", () => {
|
|
164
|
-
return this.isQueuePolling;
|
|
165
|
-
});
|
|
166
|
-
/**
|
|
167
|
-
* 獲取待分配任務隊列中的任務總數
|
|
168
|
-
* @returns {number} 所有優先級隊列的任務總數
|
|
169
|
-
*/
|
|
170
|
-
(0, _defineProperty2.default)(this, "getCountOfAssignTaskInQueue", () => {
|
|
171
|
-
let size = 0;
|
|
172
|
-
for (const prior of _configerer.configerer.POOLLER_PRIORITY) {
|
|
173
|
-
size += this.queueOfAssignTask[prior].length;
|
|
174
|
-
}
|
|
175
|
-
return size;
|
|
176
|
-
});
|
|
177
|
-
/**
|
|
178
|
-
* 添加任務到隊列
|
|
179
|
-
*
|
|
180
|
-
* @param {Function} task - 要執行的異步任務函數
|
|
181
|
-
* @param {string} priority - 優先級 ('low'|'medium'|'high'),3:low, 2:medium, 1:high
|
|
182
|
-
* @returns {string} 任務的唯一 hash,可用於後續刪除任務
|
|
183
|
-
* @throws {ERROR} 如果 task 不是函數或 priority 不合法
|
|
184
|
-
*/
|
|
185
|
-
(0, _defineProperty2.default)(this, "add", (task, priority = "low") => {
|
|
186
|
-
if (typeof task === "function") {
|
|
187
|
-
if (_configerer.configerer.POOLLER_PRIORITY.indexOf(priority) < 0) {
|
|
188
|
-
throw new _exceptioner.default(4001, `priority can't be ${priority}`);
|
|
189
|
-
}
|
|
190
|
-
const hash = _index2.utiller.getRandomHash();
|
|
191
|
-
const taskInfo = {
|
|
192
|
-
task,
|
|
193
|
-
hash
|
|
194
|
-
};
|
|
195
|
-
this.appendHashTaskMap(taskInfo);
|
|
196
|
-
this.queueOfAssignTask[priority].push(taskInfo);
|
|
197
|
-
return hash;
|
|
198
|
-
} else {
|
|
199
|
-
throw new _exceptioner.default(4002, `task can't be ${typeof task}`);
|
|
200
|
-
}
|
|
201
|
-
});
|
|
202
|
-
/**
|
|
203
|
-
* 更新執行中任務的狀態
|
|
204
|
-
* 將任務狀態從 'NOT' 更新為 'ING'
|
|
205
|
-
*
|
|
206
|
-
* @param {string} hash - 任務的 hash
|
|
207
|
-
*/
|
|
208
|
-
(0, _defineProperty2.default)(this, "updateExecuteTaskState", hash => {
|
|
209
|
-
const task = _lodash.default.find(this.queueOfExecutingTask, each => _lodash.default.isEqual(each.hash, hash));
|
|
210
|
-
if (task) {
|
|
211
|
-
this.printLogMessage(`847875153, 客端委託的任務: ${hash},更改狀態為 'ING'`);
|
|
212
|
-
task.state = "ING";
|
|
213
|
-
}
|
|
214
|
-
});
|
|
215
|
-
/**
|
|
216
|
-
* 任務包裝器
|
|
217
|
-
* 這是核心方法,用於包裝客戶端委託的任務,提供超時和錯誤處理機制
|
|
218
|
-
*
|
|
219
|
-
* 設計思路:
|
|
220
|
-
* 1. 使用兩個 Promise:一個用於超時控制,一個用於執行實際任務
|
|
221
|
-
* 2. 通過 Promise.race 實現超時機制
|
|
222
|
-
* 3. 捕獲任務執行過程中的錯誤
|
|
223
|
-
* 4. 處理任務完成後的清理工作
|
|
224
|
-
*
|
|
225
|
-
* @param {Function} assignedTask - 客戶端委託的異步任務
|
|
226
|
-
* @param {string} hashOfTask - 任務的唯一標識
|
|
227
|
-
* @param {*} param - 傳遞給任務的參數
|
|
228
|
-
* @returns {Function} 返回一個可執行的任務函數
|
|
229
|
-
*
|
|
230
|
-
* [邏輯漏洞修復] 改進錯誤處理,確保所有錯誤都能被正確捕獲和處理
|
|
231
|
-
*/
|
|
232
|
-
(0, _defineProperty2.default)(this, "taskWrapper", (assignedTask, hashOfTask, param) => () => {
|
|
233
|
-
const self = this;
|
|
234
|
-
let timeoutHash = "";
|
|
235
|
-
let assignedTaskResult;
|
|
236
|
-
let assignedTaskError;
|
|
237
|
-
let isAssignedTaskCompleted = true;
|
|
238
|
-
return new Promise((resolve, reject) => {
|
|
239
|
-
// 設置超時計時器
|
|
240
|
-
if (self.enableOfTaskTimeout) {
|
|
241
|
-
timeoutHash = setTimeout(() => {
|
|
242
|
-
try {
|
|
243
|
-
this.printLogMessage(`982532, taskWrapper執行中,發生timeout: ${self.timeOfTaskTimeout} ms`);
|
|
244
|
-
throw new _exceptioner.default(4010, self.getPoollerLogFormat(`TASK HASH:${hashOfTask} IS TIMEOUT ${self.timeOfTaskTimeout} ms ${param ? `,PARAMS IS ${JSON.stringify(param)}` : ""}`));
|
|
245
|
-
} catch (error) {
|
|
246
|
-
reject(error);
|
|
247
|
-
}
|
|
248
|
-
}, self.timeOfTaskTimeout);
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
// 執行客戶端委託的任務
|
|
252
|
-
this.printLogMessage(`984135, 客端委託的task開始執行 ${hashOfTask}`);
|
|
253
|
-
self.updateExecuteTaskState(hashOfTask);
|
|
254
|
-
assignedTask(param).then(result => {
|
|
255
|
-
this.printLogMessage(`984545, 客端委託的任務(TASK HASH:${hashOfTask}),resolve回應: ${result}`);
|
|
256
|
-
assignedTaskResult = result;
|
|
257
|
-
isAssignedTaskCompleted = true;
|
|
258
|
-
}).catch(error => {
|
|
259
|
-
this.printLogMessage(`989652, 客端委託的任務,reject回應: ${error.message}`, true, error);
|
|
260
|
-
assignedTaskError = error;
|
|
261
|
-
isAssignedTaskCompleted = false;
|
|
262
|
-
}).finally(() => {
|
|
263
|
-
clearTimeout(timeoutHash);
|
|
264
|
-
resolve();
|
|
265
|
-
this.printLogMessage(`98942,(TASK HASH:${hashOfTask}) taskWrapper()裡面第一個promise(為了timeout設計)完成了`);
|
|
266
|
-
});
|
|
267
|
-
}).then(() => {
|
|
268
|
-
// 能走到這裡,代表沒有 timeout 的狀況下執行了委託的任務
|
|
269
|
-
if (!isAssignedTaskCompleted) {
|
|
270
|
-
throw assignedTaskError;
|
|
271
|
-
} else {
|
|
272
|
-
this.printLogMessage(`9894841,(TASK HASH:${hashOfTask}) taskWrapper()裡面第二個promise(整個任務)完成了`);
|
|
273
|
-
return `${this.getLogMessageOfTaskHash(hashOfTask)} completed`;
|
|
274
|
-
}
|
|
275
|
-
}).catch(error => {
|
|
276
|
-
// 如果發生 timeout 或是客端任務掉進去 catch 都會跑到這裡
|
|
277
|
-
isAssignedTaskCompleted = false;
|
|
278
|
-
assignedTaskError = error;
|
|
279
|
-
|
|
280
|
-
// [修復] 改進錯誤處理邏輯
|
|
281
|
-
if (!self.isWait4ResultTask(hashOfTask)) {
|
|
282
|
-
if (self.handlerOfAssignTaskFail !== undefined) {
|
|
283
|
-
// 使用錯誤處理器處理錯誤,不再拋出
|
|
284
|
-
try {
|
|
285
|
-
self.handlerOfAssignTaskFail(assignedTaskError);
|
|
286
|
-
} catch (handlerError) {
|
|
287
|
-
// 如果錯誤處理器本身出錯,記錄但不拋出
|
|
288
|
-
this.printLogMessage(`錯誤處理器執行失敗: ${handlerError.message}`, true, handlerError);
|
|
289
|
-
}
|
|
290
|
-
} else {
|
|
291
|
-
// 沒有錯誤處理器時,記錄錯誤但不拋出(避免未處理的 rejection)
|
|
292
|
-
this.printLogMessage(`任務執行失敗但未設置錯誤處理器: ${assignedTaskError.message}`, true, assignedTaskError);
|
|
293
|
-
}
|
|
294
|
-
}
|
|
295
|
-
}).finally(() => {
|
|
296
|
-
const result = {
|
|
297
|
-
assignedTaskCompleted: isAssignedTaskCompleted,
|
|
298
|
-
resolve: assignedTaskResult,
|
|
299
|
-
reject: assignedTaskError
|
|
300
|
-
};
|
|
301
|
-
self.removeResolveOrRejectPromiseByHash(hashOfTask, result);
|
|
302
|
-
this.printLogMessage(`98943213, ${this.getLogMessageOfTaskHash(hashOfTask)} taskWrapper()裡面第2個promise完成了`, false, result);
|
|
303
|
-
});
|
|
304
|
-
});
|
|
305
|
-
/**
|
|
306
|
-
* 批量添加任務
|
|
307
|
-
* @param {Array<Function>} tasks - 異步任務函數數組
|
|
308
|
-
* @param {string} priority - 優先級
|
|
309
|
-
* @returns {Array<string>} 任務 hash 數組
|
|
310
|
-
* @throws {ERROR} 如果 tasks 不是數組
|
|
311
|
-
*/
|
|
312
|
-
(0, _defineProperty2.default)(this, "adds", (tasks, priority = "low") => {
|
|
313
|
-
const hashes = [];
|
|
314
|
-
if (_lodash.default.isArray(tasks)) {
|
|
315
|
-
for (const task of tasks) {
|
|
316
|
-
hashes.push(this.add(task, priority));
|
|
317
|
-
}
|
|
318
|
-
} else {
|
|
319
|
-
throw new _exceptioner.default(4003, `should be async function array, not ${typeof tasks}`);
|
|
320
|
-
}
|
|
321
|
-
return hashes;
|
|
322
|
-
});
|
|
323
|
-
/**
|
|
324
|
-
* 從映射表中移除任務
|
|
325
|
-
* @param {string} hash - 任務的 hash
|
|
326
|
-
*/
|
|
327
|
-
(0, _defineProperty2.default)(this, "removeTaskMapByHash", hash => {
|
|
328
|
-
delete this.mapOfHashNTask[hash];
|
|
329
|
-
});
|
|
330
|
-
/**
|
|
331
|
-
* 運行前的準備工作
|
|
332
|
-
* 設置輪詢標記為 true
|
|
333
|
-
*/
|
|
334
|
-
(0, _defineProperty2.default)(this, "beforeRun", () => {
|
|
335
|
-
this.isQueuePolling = true;
|
|
336
|
-
});
|
|
337
|
-
/**
|
|
338
|
-
* 運行後的清理工作
|
|
339
|
-
* 清除所有緩存
|
|
340
|
-
*/
|
|
341
|
-
(0, _defineProperty2.default)(this, "afterRun", () => {
|
|
342
|
-
this.clearCache();
|
|
343
|
-
});
|
|
344
|
-
/**
|
|
345
|
-
* 無限運行模式
|
|
346
|
-
*
|
|
347
|
-
* interval 是當 queueOfExecutingTask 滿時任務之間的間隔時間
|
|
348
|
-
* 默認情況下,如果 intervalOfQueueSleep 超過 100 次,池會關閉
|
|
349
|
-
*
|
|
350
|
-
* @param {Function|Array<Function>} task - 單個任務函數或任務數組
|
|
351
|
-
* @param {number|{min: number, max: number}} interval - 任務間隔時間
|
|
352
|
-
*/
|
|
353
|
-
(0, _defineProperty2.default)(this, "runInInfinite", async (task = [], interval) => {
|
|
354
|
-
this.beforeRun();
|
|
355
|
-
if (_lodash.default.isFunction(task)) this.add(task);else if (_lodash.default.isArray(task)) this.adds(task);else throw new _exceptioner.default(4006, `type of task is ===> ${typeof task}`);
|
|
356
|
-
this.enableTaskSleepInterval(_lodash.default.isNumber(interval), interval);
|
|
357
|
-
this.setState(_configerer.configerer.POOLLER_STATE.RUN_INFINITE);
|
|
358
|
-
while (!this.ruleOfStopInfiniteRun()) {
|
|
359
|
-
this.printLogMessage(`415123, runInInfinite() 正在無限Loop中, ${this.getLogMessageOfExecutingTaskQueueCount()}`);
|
|
360
|
-
await _classPrivateFieldGet(_run, this).call(this);
|
|
361
|
-
}
|
|
362
|
-
});
|
|
363
|
-
/**
|
|
364
|
-
* 判斷是否應該停止無限運行
|
|
365
|
-
* 規則:池已停止 且 執行隊列為空
|
|
366
|
-
*
|
|
367
|
-
* 設計理念:即使調用了 terminate(),也要等待所有正在執行的任務完成
|
|
368
|
-
*
|
|
369
|
-
* @returns {boolean} true 表示應該停止
|
|
370
|
-
*/
|
|
371
|
-
(0, _defineProperty2.default)(this, "ruleOfStopInfiniteRun", () => {
|
|
372
|
-
return !this.isRunning() && this.isExecutingTaskQueueEmpty();
|
|
373
|
-
});
|
|
374
|
-
/**
|
|
375
|
-
* 檢查執行隊列是否為空
|
|
376
|
-
* @returns {boolean} true 表示隊列為空
|
|
377
|
-
*/
|
|
378
|
-
(0, _defineProperty2.default)(this, "isExecutingTaskQueueEmpty", () => {
|
|
379
|
-
return _lodash.default.size(this.queueOfExecutingTask) === 0;
|
|
380
|
-
});
|
|
381
|
-
/**
|
|
382
|
-
* 將參數添加到等待隊列
|
|
383
|
-
* 用於 runByParams 模式
|
|
384
|
-
*
|
|
385
|
-
* @param {...any} params - 要添加的參數
|
|
386
|
-
*/
|
|
387
|
-
(0, _defineProperty2.default)(this, "appendParamInToQueue", (...params) => {
|
|
388
|
-
this.triggerBgInstance();
|
|
389
|
-
this.queueOfWaitingParam.push(...params);
|
|
390
|
-
});
|
|
391
|
-
/**
|
|
392
|
-
* 按參數運行模式
|
|
393
|
-
*
|
|
394
|
-
* 為每個參數執行一次任務函數
|
|
395
|
-
* 參數可能是 undefined,需要在 functionOfAsyncTask 中判斷
|
|
396
|
-
*
|
|
397
|
-
* @param {Function} functionOfAsyncTask - 任務函數(接受一個參數)
|
|
398
|
-
* @param {...any} params - 要處理的參數列表
|
|
399
|
-
*/
|
|
400
|
-
(0, _defineProperty2.default)(this, "runByParams", async (functionOfAsyncTask, ...params) => {
|
|
401
|
-
if (functionOfAsyncTask === undefined) {
|
|
402
|
-
functionOfAsyncTask = this.queueOfAssignTask["low"].shift().task;
|
|
403
|
-
}
|
|
404
|
-
if (!_lodash.default.isFunction(functionOfAsyncTask)) throw new _exceptioner.default(4006, `runByParams error, typeof task can't be ${typeof functionOfAsyncTask}`);
|
|
405
|
-
if (!_lodash.default.isArray(params)) throw new _exceptioner.default(4006, `runByParams error, typeof params can't be ${typeof params}`);
|
|
406
|
-
this.beforeRun();
|
|
407
|
-
this.add(functionOfAsyncTask);
|
|
408
|
-
this.appendParamInToQueue(...params);
|
|
409
|
-
this.setState(_configerer.configerer.POOLLER_STATE.RUN_BY_PARAMS);
|
|
410
|
-
|
|
411
|
-
// [修復] 只要還有參數待處理,就繼續調度
|
|
412
|
-
while (_lodash.default.size(this.queueOfWaitingParam) > 0) {
|
|
413
|
-
await _classPrivateFieldGet(_run, this).call(this);
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
// [新增] 等待所有執行中的任務完成
|
|
417
|
-
while (!this.isExecutingTaskQueueEmpty()) {
|
|
418
|
-
await _index2.utiller.syncDelay(100); // 短暫等待,避免空轉消耗CPU
|
|
419
|
-
this.printLogMessage(`等待執行中的任務完成,剩餘: ${_lodash.default.size(this.queueOfExecutingTask)}`);
|
|
420
|
-
}
|
|
421
|
-
this.printLogMessage(`951281952, runByParams() 結束了while()`);
|
|
422
|
-
this.terminate();
|
|
423
|
-
});
|
|
424
|
-
/**
|
|
425
|
-
* 按每個任務運行模式
|
|
426
|
-
*
|
|
427
|
-
* 執行提供的所有任務,任務完成後池會自動停止
|
|
428
|
-
*
|
|
429
|
-
* @param {Array<Function>} tasks - 要執行的任務數組
|
|
430
|
-
*/
|
|
431
|
-
(0, _defineProperty2.default)(this, "runByEachTask", async (tasks = []) => {
|
|
432
|
-
this.id = _index2.utiller.getRandomHash(15);
|
|
433
|
-
this.beforeRun();
|
|
434
|
-
this.adds(tasks);
|
|
435
|
-
this.setState(_configerer.configerer.POOLLER_STATE.RUN_BY_EACH_TASK);
|
|
436
|
-
while (!this.ruleOfStopInfiniteRun()) {
|
|
437
|
-
await _classPrivateFieldGet(_run, this).call(this, this.id);
|
|
438
|
-
|
|
439
|
-
// 如果待執行任務隊列已清空,終止池
|
|
440
|
-
if (this.getCountOfAssignTaskInQueue() <= 0) {
|
|
441
|
-
this.terminate();
|
|
442
|
-
this.printLogMessage(`788121, runByEachTask() 因為 taskOfWaitingQueue 清空而停止`);
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
// 為了讓 while 不要停止運算 !this.ruleOfStopInfiniteRun()
|
|
446
|
-
await _index2.utiller.syncDelay(10);
|
|
447
|
-
this.printLogMessage(`788143, runByEachTask() 為了讓while不要停止運算`);
|
|
448
|
-
}
|
|
449
|
-
this.printLogMessage(`7881952, runByEachTask() 結束了while()`);
|
|
450
|
-
});
|
|
451
|
-
/**
|
|
452
|
-
* 按次數運行模式
|
|
453
|
-
*
|
|
454
|
-
* 執行指定次數的任務,任務會循環執行並按順序同步
|
|
455
|
-
* 注意:目前只支援 1 個 worker
|
|
456
|
-
*
|
|
457
|
-
* @param {Function} functionOfAsyncTask - 要執行的任務函數
|
|
458
|
-
* @param {number} times - 執行次數
|
|
459
|
-
*
|
|
460
|
-
* [邏輯漏洞] 應該檢查 maximumOfWorker 是否為 1,否則可能出現意外行為
|
|
461
|
-
*/
|
|
462
|
-
(0, _defineProperty2.default)(this, "runByTimes", async (functionOfAsyncTask, times = 1) => {
|
|
463
|
-
// [建議] 添加 worker 數量檢查
|
|
464
|
-
if (this.maximumOfWorker !== 1) {
|
|
465
|
-
this.printLogMessage(`警告: runByTimes 模式建議使用 1 個 worker,當前為 ${this.maximumOfWorker}`, true);
|
|
466
|
-
}
|
|
467
|
-
this.countsOfRunByTimes = times;
|
|
468
|
-
this.add(functionOfAsyncTask);
|
|
469
|
-
this.beforeRun();
|
|
470
|
-
this.setState(_configerer.configerer.POOLLER_STATE.RUN_BY_TIMES);
|
|
471
|
-
while (!this.ruleOfStopInfiniteRun() && this.countsOfRunByTimes > 0) {
|
|
472
|
-
await _classPrivateFieldGet(_run, this).call(this);
|
|
473
|
-
}
|
|
474
|
-
});
|
|
475
|
-
/**
|
|
476
|
-
* 在背景運行
|
|
477
|
-
*
|
|
478
|
-
* 使用 setTimeout 在背景執行異步函數,類似於線程在背景運行
|
|
479
|
-
*
|
|
480
|
-
* @param {Function} asyncfunc - 要執行的異步函數
|
|
481
|
-
* @param {...any} params - 函數參數
|
|
482
|
-
* @returns {number} setTimeout 的 ID
|
|
483
|
-
* @throws {ERROR} 如果 asyncfunc 不是函數
|
|
484
|
-
*/
|
|
485
|
-
(0, _defineProperty2.default)(this, "runInBackGround", (asyncfunc, ...params) => {
|
|
486
|
-
this.isRunInBackgroundMode = true;
|
|
487
|
-
if (!(typeof asyncfunc === "function")) {
|
|
488
|
-
throw new _exceptioner.default(4002, `_asyncfunc can't be ${typeof asyncfunc}`);
|
|
489
|
-
}
|
|
490
|
-
return setTimeout(async () => {
|
|
491
|
-
try {
|
|
492
|
-
await asyncfunc.apply(this, params); // 確保 'this' 指向 InfinitePool
|
|
493
|
-
} catch (error) {
|
|
494
|
-
// Promise Rejection 可能已經被 taskWrapper 或其他地方捕獲
|
|
495
|
-
if (error instanceof _exceptioner.default) {
|
|
496
|
-
this.printLogMessage(`7812123, runInBackGround() 執行錯誤: ${error.message}`, true, error);
|
|
497
|
-
} else {
|
|
498
|
-
throw new _exceptioner.default(4009, {
|
|
499
|
-
message: `${this.getPoollerLogFormat("")}`
|
|
500
|
-
}, error);
|
|
501
|
-
}
|
|
502
|
-
} finally {
|
|
503
|
-
this.terminate();
|
|
504
|
-
this.printLogMessage(`7812123, runInBackGround() 走到finally`);
|
|
505
|
-
}
|
|
506
|
-
}, 1);
|
|
507
|
-
});
|
|
508
|
-
/**
|
|
509
|
-
* 獲取格式化的池日誌消息
|
|
510
|
-
* @param {string} msg - 消息內容
|
|
511
|
-
* @returns {string} 格式化的日誌消息
|
|
512
|
-
*/
|
|
513
|
-
(0, _defineProperty2.default)(this, "getPoollerLogFormat", msg => {
|
|
514
|
-
return `POOLLER NAME: ${this.getPoolId()}${_lodash.default.isEmpty(msg) ? "" : " , "}${msg}`;
|
|
515
|
-
});
|
|
516
|
-
/**
|
|
517
|
-
* 設置任務失敗處理器
|
|
518
|
-
*
|
|
519
|
-
* 如果不設置,任務失敗會導致整個池停止
|
|
520
|
-
*
|
|
521
|
-
* @param {Function} listener - 錯誤處理函數,接收 error 對象
|
|
522
|
-
*/
|
|
523
|
-
(0, _defineProperty2.default)(this, "setTaskFailHandler", (listener = error => console.log(error.message)) => {
|
|
524
|
-
this.handlerOfAssignTaskFail = listener;
|
|
525
|
-
});
|
|
526
|
-
/**
|
|
527
|
-
* 將已分配任務添加到執行隊列的規則
|
|
528
|
-
*
|
|
529
|
-
* 根據不同的運行模式返回不同的判斷規則:
|
|
530
|
-
* - RUN_BY_EACH_TASK: 池運行中 且 執行隊列未滿 且 有待分配任務
|
|
531
|
-
* - RUN_BY_PARAMS: 池運行中 且 執行隊列未滿 且 有待處理參數
|
|
532
|
-
* - RUN_BY_TIMES/RUN_INFINITE: 池運行中 且 執行隊列未滿 且 有任務
|
|
533
|
-
*
|
|
534
|
-
* @returns {boolean} true 表示可以添加任務到執行隊列
|
|
535
|
-
* @throws {ERROR} 如果狀態未知
|
|
536
|
-
*/
|
|
537
|
-
(0, _defineProperty2.default)(this, "rulesOfAppendToExecutingTask", () => {
|
|
538
|
-
switch (this.state) {
|
|
539
|
-
case _configerer.configerer.POOLLER_STATE.RUN_BY_EACH_TASK:
|
|
540
|
-
return this.isRunning() && !this.isExecutingQueueFull() && this.getCountOfAssignTaskInQueue() > 0;
|
|
541
|
-
case _configerer.configerer.POOLLER_STATE.RUN_BY_PARAMS:
|
|
542
|
-
return this.isRunning() && !this.isExecutingQueueFull() && _lodash.default.size(this.queueOfWaitingParam) > 0;
|
|
543
|
-
case _configerer.configerer.POOLLER_STATE.RUN_BY_TIMES:
|
|
544
|
-
case _configerer.configerer.POOLLER_STATE.RUN_INFINITE:
|
|
545
|
-
// 這些模式的任務數在 AssignTaskQueue 中只有一個或一組
|
|
546
|
-
return this.isRunning() && !this.isExecutingQueueFull() && this.getCountOfAssignTaskInQueue() > 0;
|
|
547
|
-
default:
|
|
548
|
-
throw new _exceptioner.default(4005, `this.state ==> ${this.state}`);
|
|
549
|
-
}
|
|
550
|
-
});
|
|
551
|
-
/**
|
|
552
|
-
* 將任務添加到執行隊列
|
|
553
|
-
*
|
|
554
|
-
* @param {string} hash - 任務的 hash
|
|
555
|
-
* @param {Promise} promise - 任務的 Promise
|
|
556
|
-
*/
|
|
557
|
-
(0, _defineProperty2.default)(this, "appendTaskToExecuteQueue", (hash, promise) => {
|
|
558
|
-
// 對於 RUN_BY_TIMES 模式,遞減計數
|
|
559
|
-
if (_lodash.default.isEqual(this.state, _configerer.configerer.POOLLER_STATE.RUN_BY_TIMES)) {
|
|
560
|
-
this.countsOfRunByTimes = this.countsOfRunByTimes - 1;
|
|
561
|
-
}
|
|
562
|
-
const task = {
|
|
563
|
-
state: "NOT",
|
|
564
|
-
hash: hash,
|
|
565
|
-
task: promise
|
|
566
|
-
};
|
|
567
|
-
this.printLogMessage(`4484451, 增加了一個assignedTask ${this.getLogMessageOfTaskHash(hash)} 到 QueueOfExecutingTask ,${this.getLogMessageOfExecutingTaskQueueCount()}`, false, task);
|
|
568
|
-
this.queueOfExecutingTask.push(task);
|
|
569
|
-
});
|
|
570
|
-
/**
|
|
571
|
-
* 獲取執行隊列計數的日誌消息
|
|
572
|
-
* @returns {string} 格式化的日誌消息
|
|
573
|
-
*/
|
|
574
|
-
(0, _defineProperty2.default)(this, "getLogMessageOfExecutingTaskQueueCount", () => {
|
|
575
|
-
return `ExecutingTaskQueueCount: ${_lodash.default.size(this.queueOfExecutingTask)}`;
|
|
576
|
-
});
|
|
577
|
-
/**
|
|
578
|
-
* 獲取分配隊列計數的日誌消息
|
|
579
|
-
* @returns {string} 格式化的日誌消息
|
|
580
|
-
*/
|
|
581
|
-
(0, _defineProperty2.default)(this, "getLogMessageOfAssignTaskQueueCount", () => {
|
|
582
|
-
return `AssignTaskQueueCount: ${this.getCountOfAssignTaskInQueue()}`;
|
|
583
|
-
});
|
|
584
|
-
/**
|
|
585
|
-
* 獲取任務 hash 的日誌消息
|
|
586
|
-
* @param {string} hash - 任務的 hash
|
|
587
|
-
* @returns {string} 格式化的日誌消息
|
|
588
|
-
*/
|
|
589
|
-
(0, _defineProperty2.default)(this, "getLogMessageOfTaskHash", hash => {
|
|
590
|
-
return `TASK HASH: ${hash}`;
|
|
591
|
-
});
|
|
592
|
-
/**
|
|
593
|
-
* 顯示池的當前狀態
|
|
594
|
-
*
|
|
595
|
-
* 輸出:
|
|
596
|
-
* - Worker 數量
|
|
597
|
-
* - 待分配任務數量
|
|
598
|
-
* - 正在執行的任務數量
|
|
599
|
-
* - TaskMap 中的任務數量
|
|
600
|
-
*/
|
|
601
|
-
(0, _defineProperty2.default)(this, "showState", () => {
|
|
602
|
-
_index2.utiller.appendInfo(this.getPoollerLogFormat(`workerCount: ${this.maximumOfWorker}`));
|
|
603
|
-
_index2.utiller.appendInfo(this.getPoollerLogFormat(`taskQueue(還在排隊的Task): ${this.getCountOfAssignTaskInQueue()}`));
|
|
604
|
-
_index2.utiller.appendInfo(this.getPoollerLogFormat(`QueueOfExecutingTask(正在執行的AsyncTask, 超過workerCount就是bug): ${_lodash.default.size(this.queueOfExecutingTask)}`));
|
|
605
|
-
_index2.utiller.appendInfo(this.getPoollerLogFormat(`mapOfHashNTask(還沒執行到的AsyncTask reference的暫存區): ${_lodash.default.size(this.mapOfHashNTask)}`));
|
|
606
|
-
});
|
|
607
|
-
/**
|
|
608
|
-
* 核心運行方法(私有)
|
|
609
|
-
*
|
|
610
|
-
* 執行流程:
|
|
611
|
-
* 1. 調度任務(syncTaskDispatcher)
|
|
612
|
-
* 2. 執行所有狀態為 'NOT' 的任務
|
|
613
|
-
* 3. 使用 Promise.race 等待任一任務完成
|
|
614
|
-
* 4. 檢查執行隊列大小(調試用)
|
|
615
|
-
*
|
|
616
|
-
* @param {string} id - 運行實例的 ID(可選)
|
|
617
|
-
*/
|
|
618
|
-
_classPrivateFieldInitSpec(this, _run, async () => {
|
|
619
|
-
const self = this;
|
|
620
|
-
|
|
621
|
-
/**
|
|
622
|
-
* 執行所有待執行的任務
|
|
623
|
-
* 使用 Promise.race 實現並發控制
|
|
624
|
-
*/
|
|
625
|
-
async function execute() {
|
|
626
|
-
const tasks = _lodash.default.filter(self.queueOfExecutingTask, each => _lodash.default.isEqual(each.state, "NOT")).map(each => {
|
|
627
|
-
const taskWrapper = each.task;
|
|
628
|
-
return taskWrapper();
|
|
629
|
-
});
|
|
630
|
-
self.printLogMessage(`454652321, 開始任務(taskWrapper): run() 裡面的execute開始執行, task(state = NOT)的長度 ${_lodash.default.size(tasks)}`);
|
|
631
|
-
const result = _lodash.default.size(tasks) > 0 ? await Promise.race(tasks) : "4542131684, task is empty";
|
|
632
|
-
self.printLogMessage(`54121445161, 結束任務(taskWrapper): run() 裡面的execute結束執行`);
|
|
633
|
-
return result;
|
|
634
|
-
}
|
|
635
|
-
|
|
636
|
-
/**
|
|
637
|
-
* 空任務(當 worker 數量為 0 時使用)
|
|
638
|
-
*/
|
|
639
|
-
async function emptyTask() {
|
|
640
|
-
self.printLogMessage(`因為max count of worker為0,所以指派一個簡單的任務`);
|
|
641
|
-
await _index2.utiller.syncDelay(10);
|
|
642
|
-
}
|
|
643
|
-
|
|
644
|
-
// 調度任務
|
|
645
|
-
await this.syncTaskDispatcher();
|
|
646
|
-
if (this.maximumOfWorker === 0) {
|
|
647
|
-
// 當 maximumOfWorker 為 0 時,setTimeout/syncDelay() 會卡住
|
|
648
|
-
// 給了 emptyTask() 就能閃過這個 issue
|
|
649
|
-
await emptyTask();
|
|
650
|
-
} else if (!this.isExecutingTaskQueueEmpty()) {
|
|
651
|
-
// 當池已經被要求停止時,executeQueue 裡面還有未做完的任務
|
|
652
|
-
this.printLogMessage(`4512211, 開始任務(taskWrapper): ${this.getLogMessageOfExecutingTaskQueueCount()}`);
|
|
653
|
-
const task = await execute();
|
|
654
|
-
this.printLogMessage(`4512213 完畢任務(taskWrapper:${task}), ${this.getLogMessageOfExecutingTaskQueueCount()}, ${this.getLogMessageOfAssignTaskQueueCount()}`);
|
|
655
|
-
} else {
|
|
656
|
-
this.printLogMessage(`4574152 不應該走到這裏,但是 minor issue`, true);
|
|
657
|
-
}
|
|
658
|
-
|
|
659
|
-
// 調試檢查:執行隊列不應該超過 worker 數量
|
|
660
|
-
if (this.queueOfExecutingTask.length > this.maximumOfWorker) this.printLogMessage(`4512214 一定是改壞了!!!!!!!!!!, ${this.getLogMessageOfExecutingTaskQueueCount} `, true);
|
|
661
|
-
self.printLogMessage(`5478421212, 離開 run()`);
|
|
662
|
-
});
|
|
663
|
-
/**
|
|
664
|
-
* 輔助方法:獲取用於重複執行模式的任務資訊
|
|
665
|
-
*
|
|
666
|
-
* 對於需要重複執行的模式(INFINITE/TIMES/PARAMS),不能直接使用原始的 taskInfo
|
|
667
|
-
* 必須複製任務函數並賦予新的 hash,用於在 executingTaskQueue 中追蹤
|
|
668
|
-
*
|
|
669
|
-
* @param {Object} originalTaskInfo - 原始任務信息 {task, hash}
|
|
670
|
-
* @returns {Object} 新的任務信息,包含新的 hash
|
|
671
|
-
*/
|
|
672
|
-
(0, _defineProperty2.default)(this, "getTaskInfoForRepetitiveRun", originalTaskInfo => {
|
|
673
|
-
const newTaskInfo = {
|
|
674
|
-
task: originalTaskInfo.task,
|
|
675
|
-
hash: _index2.utiller.getRandomHash()
|
|
676
|
-
};
|
|
677
|
-
this.appendHashTaskMap(newTaskInfo); // 追蹤這個新的 hash 直到任務開始執行
|
|
678
|
-
return newTaskInfo;
|
|
679
|
-
});
|
|
680
|
-
/**
|
|
681
|
-
* 根據優先級獲取任務信息
|
|
682
|
-
*
|
|
683
|
-
* 優先級順序:high > medium > low
|
|
684
|
-
*
|
|
685
|
-
* 根據不同的運行模式有不同的行為:
|
|
686
|
-
* - RUN_BY_EACH_TASK: 移除並返回任務(shift)
|
|
687
|
-
* - 其他模式: 複製任務並生成新 hash(peek + copy)
|
|
688
|
-
*
|
|
689
|
-
* @returns {Object} 任務信息對象 {task, hash}
|
|
690
|
-
* @throws {ERROR} 如果在非 RUN_BY_EACH_TASK 模式下隊列為空
|
|
691
|
-
*/
|
|
692
|
-
(0, _defineProperty2.default)(this, "getTaskInfoDependOnPriority", () => {
|
|
693
|
-
for (const prior of _configerer.configerer.POOLLER_PRIORITY) {
|
|
694
|
-
if (this.queueOfAssignTask[prior].length > 0) {
|
|
695
|
-
switch (this.state) {
|
|
696
|
-
case _configerer.configerer.POOLLER_STATE.RUN_BY_EACH_TASK:
|
|
697
|
-
// RUN_BY_EACH_TASK: 移除任務(每個任務只執行一次)
|
|
698
|
-
return this.queueOfAssignTask[prior].shift();
|
|
699
|
-
case _configerer.configerer.POOLLER_STATE.RUN_BY_PARAMS:
|
|
700
|
-
case _configerer.configerer.POOLLER_STATE.RUN_BY_TIMES:
|
|
701
|
-
case _configerer.configerer.POOLLER_STATE.RUN_INFINITE:
|
|
702
|
-
// 重複執行模式的處理:
|
|
703
|
-
// 1. 不使用 shift() 移除任務(需要重複執行)
|
|
704
|
-
// 2. 獲取任務函數的引用(Peek)
|
|
705
|
-
// 3. 複製任務函數並賦予新的 Hash 以便追蹤本次執行
|
|
706
|
-
const originalTaskInfo = this.queueOfAssignTask[prior][0];
|
|
707
|
-
return this.getTaskInfoForRepetitiveRun(originalTaskInfo);
|
|
708
|
-
default:
|
|
709
|
-
throw new _exceptioner.default(4005, `this.state ==> ${this.state}`);
|
|
710
|
-
}
|
|
711
|
-
}
|
|
712
|
-
}
|
|
713
|
-
|
|
714
|
-
// 如果所有優先級隊列都為空,且不是 RUN_BY_EACH_TASK 模式,拋出錯誤
|
|
715
|
-
if (!_lodash.default.isEqual(this.state, _configerer.configerer.POOLLER_STATE.RUN_BY_EACH_TASK)) throw new _exceptioner.default(4007);
|
|
716
|
-
});
|
|
717
|
-
/**
|
|
718
|
-
* 通過 hash 移除已完成的 Promise
|
|
719
|
-
*
|
|
720
|
-
* 處理流程:
|
|
721
|
-
* 1. 如果有 callback wrapper(Wait4Result 模式),調用回調
|
|
722
|
-
* 2. 從執行隊列中移除任務
|
|
723
|
-
*
|
|
724
|
-
* @param {string} hash - 任務的 hash
|
|
725
|
-
* @param {Object} result - 任務執行結果 {assignedTaskCompleted, resolve, reject}
|
|
726
|
-
*
|
|
727
|
-
* [邏輯漏洞修復] 確保即使沒有 callback 也要清理任務
|
|
728
|
-
*/
|
|
729
|
-
(0, _defineProperty2.default)(this, "removeResolveOrRejectPromiseByHash", (hash, result) => {
|
|
730
|
-
const callbackWrapper = this.mapOfHashNCallbackWrapper[hash];
|
|
731
|
-
if (callbackWrapper !== undefined) {
|
|
732
|
-
this.printLogMessage(`5644153248, removeResolveOrRejectPromiseByHash 拿掉了完成的任務(${this.getLogMessageOfTaskHash(hash)})`);
|
|
733
|
-
try {
|
|
734
|
-
callbackWrapper(result);
|
|
735
|
-
} catch (callbackError) {
|
|
736
|
-
// [修復] 捕獲回調執行錯誤,避免影響清理流程
|
|
737
|
-
this.printLogMessage(`回調執行失敗: ${callbackError.message}`, true, callbackError);
|
|
738
|
-
} finally {
|
|
739
|
-
delete this.mapOfHashNCallbackWrapper[hash];
|
|
740
|
-
}
|
|
741
|
-
}
|
|
742
|
-
this.removePromiseFromExecutingQueue(hash);
|
|
743
|
-
});
|
|
744
|
-
/**
|
|
745
|
-
* 從執行隊列中移除 Promise
|
|
746
|
-
* @param {string} hash - 任務的 hash
|
|
747
|
-
*/
|
|
748
|
-
(0, _defineProperty2.default)(this, "removePromiseFromExecutingQueue", hash => {
|
|
749
|
-
this.printLogMessage(`56448412, QueueOfExecutingTask 拿掉了完成的任務 ${this.getLogMessageOfTaskHash(hash)}`);
|
|
750
|
-
_lodash.default.remove(this.queueOfExecutingTask, each => _lodash.default.isEqual(hash, each.hash));
|
|
751
|
-
});
|
|
752
|
-
/**
|
|
753
|
-
* 在背景以無限模式運行
|
|
754
|
-
* @param {Function} functionOfAsyncTask - 任務函數
|
|
755
|
-
* @param {number|Object} interval - 任務間隔
|
|
756
|
-
* @returns {InfinitePool} 返回實例本身(鏈式調用)
|
|
757
|
-
*/
|
|
758
|
-
(0, _defineProperty2.default)(this, "runInfiniteInBackground", (functionOfAsyncTask, interval) => {
|
|
759
|
-
return this.invokeInstanceOfBackground(this.runInInfinite, functionOfAsyncTask, interval);
|
|
760
|
-
});
|
|
761
|
-
/**
|
|
762
|
-
* 在背景以參數模式運行
|
|
763
|
-
* @param {Function} functionOfAsyncTask - 任務函數
|
|
764
|
-
* @param {...any} params - 參數列表
|
|
765
|
-
* @returns {InfinitePool} 返回實例本身(鏈式調用)
|
|
766
|
-
*/
|
|
767
|
-
(0, _defineProperty2.default)(this, "runByParamInBackGround", (functionOfAsyncTask, ...params) => {
|
|
768
|
-
return this.invokeInstanceOfBackground(this.runByParams, functionOfAsyncTask, ...params);
|
|
769
|
-
});
|
|
770
|
-
/**
|
|
771
|
-
* 在背景以次數模式運行
|
|
772
|
-
* @param {Function} functionOfAsyncTask - 任務函數
|
|
773
|
-
* @param {number} times - 執行次數
|
|
774
|
-
* @returns {InfinitePool} 返回實例本身(鏈式調用)
|
|
775
|
-
*/
|
|
776
|
-
(0, _defineProperty2.default)(this, "runByTimesInBackGround", (functionOfAsyncTask, times) => {
|
|
777
|
-
return this.invokeInstanceOfBackground(this.runByTimes, functionOfAsyncTask, times);
|
|
778
|
-
});
|
|
779
|
-
/**
|
|
780
|
-
* 在背景以每個任務模式運行
|
|
781
|
-
* @param {...any} params - 參數(任務數組)
|
|
782
|
-
* @returns {InfinitePool} 返回實例本身(鏈式調用)
|
|
783
|
-
*/
|
|
784
|
-
(0, _defineProperty2.default)(this, "runByEachTaskInBackGround", (...params) => {
|
|
785
|
-
return this.invokeInstanceOfBackground(this.runByEachTask, ...params);
|
|
786
|
-
});
|
|
787
|
-
/**
|
|
788
|
-
* 調用背景實例
|
|
789
|
-
*
|
|
790
|
-
* 確保只有一個背景實例在運行
|
|
791
|
-
* 如果已有實例,先清除再創建新的
|
|
792
|
-
*
|
|
793
|
-
* @param {Function} state - 運行模式函數
|
|
794
|
-
* @param {...any} params - 參數
|
|
795
|
-
* @returns {InfinitePool} 返回實例本身(鏈式調用)
|
|
796
|
-
*
|
|
797
|
-
* 設計理念:
|
|
798
|
-
* 回傳整個 instance 是為了方便使用,可以寫成一行:
|
|
799
|
-
* const pool = new InfinitePool(1).runByEachTaskInBackGround();
|
|
800
|
-
*/
|
|
801
|
-
(0, _defineProperty2.default)(this, "invokeInstanceOfBackground", (state, ...params) => {
|
|
802
|
-
if (this.atomicBgInstance !== undefined) clearTimeout(this.atomicBgInstance);
|
|
803
|
-
this.atomicBgInstance = this.runInBackGround(state, ...params);
|
|
804
|
-
return this;
|
|
805
|
-
});
|
|
806
|
-
this.maximumOfWorker = maxWorkers;
|
|
807
|
-
this.setPoolId(_lodash.default.toString(name));
|
|
808
|
-
// 初始化優先級隊列
|
|
809
|
-
for (const prior of _configerer.configerer.POOLLER_PRIORITY) {
|
|
810
|
-
this.queueOfAssignTask[prior] = [];
|
|
811
|
-
}
|
|
812
|
-
}
|
|
813
|
-
/**
|
|
814
|
-
* @deprecated 此功能已棄用,因為沒有實際的睡眠機制
|
|
815
|
-
* 啟用基於睡眠次數的隊列終止機制
|
|
816
|
-
*/
|
|
817
|
-
enableQueueTerminateBySleepCount(enable = true, interval = _configerer.configerer.POOLLER_QUEUE_TIME_OF_SLEEP_INTERVAL_DEFAULT, times = _configerer.configerer.POOLLER_QUEUE_MAX_SLEEP_COUNTS_DEFAULT) {
|
|
818
|
-
this.enableOfQueueTerminateSleepCount = enable;
|
|
819
|
-
this.queueMaxSleepCounts = times;
|
|
820
|
-
this.intervalOfQueueSleep = interval;
|
|
821
|
-
}
|
|
822
|
-
|
|
823
|
-
/**
|
|
824
|
-
* 清除所有緩存數據
|
|
825
|
-
* 清空執行隊列、任務映射和分配隊列
|
|
826
|
-
*/
|
|
827
|
-
clearCache() {
|
|
828
|
-
this.queueOfExecutingTask.length = 0;
|
|
829
|
-
this.mapOfHashNTask = {};
|
|
830
|
-
this.queueOfAssignTask = {};
|
|
831
|
-
}
|
|
832
|
-
|
|
833
|
-
/**
|
|
834
|
-
* 終止池的運行
|
|
835
|
-
* 設置輪詢標記為 false,但不會強制停止正在執行的任務
|
|
836
|
-
*/
|
|
837
|
-
terminate() {
|
|
838
|
-
this.isQueuePolling = false;
|
|
839
|
-
}
|
|
840
|
-
|
|
841
|
-
/**
|
|
842
|
-
* 打印日誌消息(僅在調試模式下)
|
|
843
|
-
* @param {string} message - 日誌消息
|
|
844
|
-
* @param {boolean} error - 是否為錯誤消息
|
|
845
|
-
* @param {...any} infos - 額外的信息參數
|
|
846
|
-
*/
|
|
847
|
-
printLogMessage(message, error = false, ...infos) {
|
|
848
|
-
if (SPECIFICITY_DEBUG) _index2.utiller.printLogMessage(this.getPoollerLogFormat(message), error, ...infos);
|
|
849
|
-
}
|
|
850
|
-
/**
|
|
851
|
-
* 設置 worker 數量
|
|
852
|
-
* @param {number} counts - 新的 worker 數量
|
|
853
|
-
*/
|
|
854
|
-
setWorker(counts) {
|
|
855
|
-
this.maximumOfWorker = counts;
|
|
856
|
-
}
|
|
857
|
-
|
|
858
|
-
/**
|
|
859
|
-
* 清除任務間隔設置
|
|
860
|
-
* 將間隔設為 0
|
|
861
|
-
*/
|
|
862
|
-
cleanTaskInterval() {
|
|
863
|
-
this.taskSleepInterval = {
|
|
864
|
-
min: 0,
|
|
865
|
-
max: 0
|
|
866
|
-
};
|
|
867
|
-
}
|
|
868
|
-
|
|
869
|
-
/**
|
|
870
|
-
* 啟用任務睡眠間隔
|
|
871
|
-
* 用於控制任務執行頻率(如爬蟲場景)
|
|
872
|
-
*
|
|
873
|
-
* @param {boolean} enable - 是否啟用
|
|
874
|
-
* @param {number|{min: number, max: number}} interval - 間隔時間(毫秒)
|
|
875
|
-
*/
|
|
876
|
-
enableTaskSleepInterval(enable = true, interval = _configerer.configerer.POOLLER_TASK_OF_INTERVAL_DEFAULT) {
|
|
877
|
-
this.enableOfTaskSleepByInterval = enable;
|
|
878
|
-
if (_lodash.default.isNumber(interval)) {
|
|
879
|
-
interval = {
|
|
880
|
-
min: interval,
|
|
881
|
-
max: interval
|
|
882
|
-
};
|
|
883
|
-
}
|
|
884
|
-
this.taskSleepInterval = interval;
|
|
885
|
-
}
|
|
886
|
-
|
|
887
|
-
/**
|
|
888
|
-
* 啟用任務超時機制
|
|
889
|
-
* @param {boolean} enable - 是否啟用
|
|
890
|
-
* @param {number} millionSec - 超時時間(毫秒)
|
|
891
|
-
*/
|
|
892
|
-
enableTaskTimeout(enable = true, millionSec = _configerer.configerer.POOLLER_TASK_TIMEOUT_DEFAULT) {
|
|
893
|
-
this.enableOfTaskTimeout = enable;
|
|
894
|
-
this.timeOfTaskTimeout = millionSec;
|
|
895
|
-
}
|
|
896
|
-
|
|
897
|
-
/**
|
|
898
|
-
* 添加任務並等待結果
|
|
899
|
-
* 返回一個 Promise,當任務完成時 resolve 結果或 reject 錯誤
|
|
900
|
-
*
|
|
901
|
-
* @param {Function} asyncTask - 異步任務函數
|
|
902
|
-
* @param {string} priority - 優先級 ('low'|'medium'|'high')
|
|
903
|
-
* @param {string} taskName - 任務名稱(目前未使用)
|
|
904
|
-
* @returns {Promise} 任務執行結果的 Promise
|
|
905
|
-
*/
|
|
906
|
-
async addTaskAndWait4Result(asyncTask, priority = "low", taskName = "noName") {
|
|
907
|
-
this.triggerBgInstance();
|
|
908
|
-
return new Promise((resolve, reject) => {
|
|
909
|
-
const callbackWrapper = result => {
|
|
910
|
-
if (result.assignedTaskCompleted) {
|
|
911
|
-
resolve(result.resolve);
|
|
912
|
-
} else {
|
|
913
|
-
reject(result.reject);
|
|
914
|
-
}
|
|
915
|
-
};
|
|
916
|
-
const hash = this.add(asyncTask, priority);
|
|
917
|
-
this.registerHash4Result(hash, callbackWrapper);
|
|
918
|
-
});
|
|
919
|
-
}
|
|
920
|
-
|
|
921
|
-
/**
|
|
922
|
-
* 註冊 hash 對應的結果回調
|
|
923
|
-
* @param {string} hash - 任務的唯一標識
|
|
924
|
-
* @param {Function} callback - 結果回調函數
|
|
925
|
-
*/
|
|
926
|
-
registerHash4Result(hash, callback) {
|
|
927
|
-
this.mapOfHashNCallbackWrapper[hash] = callback;
|
|
928
|
-
}
|
|
929
|
-
/**
|
|
930
|
-
* 將任務信息添加到映射表
|
|
931
|
-
* @param {Object} taskInfo - 任務信息對象 {task, hash}
|
|
932
|
-
*/
|
|
933
|
-
appendHashTaskMap(taskInfo) {
|
|
934
|
-
this.mapOfHashNTask[taskInfo.hash] = taskInfo;
|
|
935
|
-
}
|
|
936
|
-
|
|
937
|
-
/**
|
|
938
|
-
* 通過 hash 獲取任務信息
|
|
939
|
-
* @param {string} hash - 任務的 hash
|
|
940
|
-
* @returns {Object} 任務信息對象
|
|
941
|
-
*/
|
|
942
|
-
getTaskInfoByHash(hash) {
|
|
943
|
-
return this.mapOfHashNTask[hash];
|
|
944
|
-
}
|
|
945
|
-
|
|
946
|
-
/**
|
|
947
|
-
* 通過 hash 從隊列中移除任務
|
|
948
|
-
*
|
|
949
|
-
* 注意:只能移除尚未執行的任務(在 assignTask 隊列中的任務)
|
|
950
|
-
* 已經進入 executing 隊列的任務無法刪除
|
|
951
|
-
*
|
|
952
|
-
* @param {string} hash - 任務創建時返回的唯一標識
|
|
953
|
-
* @returns {boolean} true 表示成功刪除,false 表示未找到或已執行
|
|
954
|
-
* @throws {ERROR} 如果 hash 對應的任務不存在
|
|
955
|
-
*
|
|
956
|
-
* [邏輯漏洞修復] 修復索引檢查錯誤,應該是 >= 0 而不是 > 0
|
|
957
|
-
*/
|
|
958
|
-
remove(hash) {
|
|
959
|
-
let taskInfo = this.getTaskInfoByHash(hash);
|
|
960
|
-
if (taskInfo) {
|
|
961
|
-
for (const prior of _configerer.configerer.POOLLER_PRIORITY) {
|
|
962
|
-
const _index = _lodash.default.indexOf(this.queueOfAssignTask[prior], taskInfo);
|
|
963
|
-
// [修復] 改為 >= 0,否則索引為 0 的任務無法刪除
|
|
964
|
-
if (_index >= 0) {
|
|
965
|
-
this.queueOfAssignTask[prior].splice(_index, 1);
|
|
966
|
-
this.removeTaskMapByHash(hash);
|
|
967
|
-
return true;
|
|
968
|
-
}
|
|
969
|
-
}
|
|
970
|
-
return false;
|
|
971
|
-
} else {
|
|
972
|
-
throw new _exceptioner.default(4004, hash);
|
|
973
|
-
}
|
|
974
|
-
}
|
|
975
|
-
/**
|
|
976
|
-
* 設置池的運行狀態
|
|
977
|
-
* @param {string} _state - 新的狀態
|
|
978
|
-
*/
|
|
979
|
-
setState(_state) {
|
|
980
|
-
this.state = _state;
|
|
981
|
-
}
|
|
982
|
-
|
|
983
|
-
/**
|
|
984
|
-
* 檢查並標記初始任務狀態
|
|
985
|
-
*
|
|
986
|
-
* 用於 disableFirstRun 機制
|
|
987
|
-
* 第一次調用返回 false 並標記為已完成
|
|
988
|
-
* 之後的調用都返回 true
|
|
989
|
-
*
|
|
990
|
-
* @returns {boolean} 初始任務是否已完成
|
|
991
|
-
*/
|
|
992
|
-
checkAndMarkInitialTaskStatus() {
|
|
993
|
-
if (!this.initialTaskCompleted) {
|
|
994
|
-
this.initialTaskCompleted = true;
|
|
995
|
-
return false;
|
|
996
|
-
}
|
|
997
|
-
return this.initialTaskCompleted;
|
|
998
|
-
}
|
|
999
|
-
|
|
1000
|
-
/**
|
|
1001
|
-
* 設置是否禁用首次立即執行
|
|
1002
|
-
*
|
|
1003
|
-
* 如果設定 interval,可通過此參數控制第一個任務是否立即執行
|
|
1004
|
-
*
|
|
1005
|
-
* @param {boolean} disable - true 表示禁用首次執行
|
|
1006
|
-
*/
|
|
1007
|
-
setDisableFirstRun(disable = true) {
|
|
1008
|
-
this.disableFirstRun = disable;
|
|
1009
|
-
}
|
|
1010
|
-
|
|
1011
|
-
/**
|
|
1012
|
-
* 觸發背景實例
|
|
1013
|
-
*
|
|
1014
|
-
* 被動式啟動機制,避免一直 while() 循環造成性能問題
|
|
1015
|
-
* 當有新任務添加時,自動觸發對應的背景運行模式
|
|
1016
|
-
*
|
|
1017
|
-
* 目前支援:RUN_BY_EACH_TASK 和 RUN_BY_PARAMS 模式
|
|
1018
|
-
*
|
|
1019
|
-
* [邏輯漏洞修復] 應該在觸發前設置 isQueuePolling,防止重複觸發
|
|
1020
|
-
*/
|
|
1021
|
-
triggerBgInstance() {
|
|
1022
|
-
if (!this.isRunInBackgroundMode || this.isQueuePolling) {
|
|
1023
|
-
return;
|
|
1024
|
-
}
|
|
1025
|
-
if (this.state === _configerer.configerer.POOLLER_STATE.RUN_BY_EACH_TASK) {
|
|
1026
|
-
// [修復] 防止 race condition,在啟動前先標記為已啟動
|
|
1027
|
-
this.runByEachTaskInBackGround();
|
|
1028
|
-
return;
|
|
1029
|
-
} else if (this.state === _configerer.configerer.POOLLER_STATE.RUN_BY_PARAMS) {
|
|
1030
|
-
this.runByParamInBackGround();
|
|
1031
|
-
return;
|
|
1032
|
-
}
|
|
1033
|
-
throw new _exceptioner.default(4011, `this.state is ==> ${_index2.utiller.getItsKeyByValue(_configerer.configerer.POOLLER_STATE, this.state)}`);
|
|
1034
|
-
}
|
|
1035
|
-
|
|
1036
|
-
/**
|
|
1037
|
-
* 檢查執行隊列是否已滿
|
|
1038
|
-
* @returns {boolean} true 表示隊列已滿
|
|
1039
|
-
*/
|
|
1040
|
-
isExecutingQueueFull() {
|
|
1041
|
-
return _lodash.default.size(this.queueOfExecutingTask) >= this.maximumOfWorker;
|
|
1042
|
-
}
|
|
1043
|
-
|
|
1044
|
-
/**
|
|
1045
|
-
* 檢查任務隊列是否為空
|
|
1046
|
-
* @returns {boolean} true 表示隊列為空
|
|
1047
|
-
*/
|
|
1048
|
-
isTaskQueueEmpty() {
|
|
1049
|
-
return this.getCountOfAssignTaskInQueue() === 0;
|
|
1050
|
-
}
|
|
1051
|
-
|
|
1052
|
-
/**
|
|
1053
|
-
* 同步任務調度器
|
|
1054
|
-
*
|
|
1055
|
-
* 核心調度邏輯:
|
|
1056
|
-
* 1. 檢查是否需要延遲(基於 disableFirstRun 和 taskSleepInterval)
|
|
1057
|
-
* 2. 根據規則將待分配任務移入執行隊列
|
|
1058
|
-
* 3. 根據任務狀態決定是否移除 taskMap
|
|
1059
|
-
*
|
|
1060
|
-
* 設計要點:
|
|
1061
|
-
* - 對於 RUN_BY_EACH_TASK,任務執行後移除 TaskMap(因為不會重複執行)
|
|
1062
|
-
* - 對於重複執行的模式,保留任務函數的引用,每次執行時生成新的 hash
|
|
1063
|
-
*/
|
|
1064
|
-
async syncTaskDispatcher() {
|
|
1065
|
-
this.printLogMessage(`448984466, 走進來了 syncTaskDispatcher()`);
|
|
1066
|
-
|
|
1067
|
-
// 檢查是否應該跳過首次執行
|
|
1068
|
-
const initialTaskShouldNotRun = this.disableFirstRun && !this.checkAndMarkInitialTaskStatus();
|
|
1069
|
-
const isExecutingTaskAlmostFull = this.queueOfExecutingTask.length >= this.maximumOfWorker - 1;
|
|
1070
|
-
// 因為能走到 syncTaskDispatcher 表示其中一個工作完成了
|
|
1071
|
-
// 這個瞬間不可能 === maximumOfWorker,所以必須減 1
|
|
1072
|
-
// 除非這個 syncTaskDispatcher 是單獨一個線程
|
|
1073
|
-
const comparison = this.checkAndMarkInitialTaskStatus() && isExecutingTaskAlmostFull && this.enableOfTaskSleepByInterval;
|
|
1074
|
-
if (initialTaskShouldNotRun || comparison) {
|
|
1075
|
-
const restInInterval = await _index2.utiller.syncDelayRandom(this.taskSleepInterval.min, this.taskSleepInterval.max);
|
|
1076
|
-
this.printLogMessage(`4484121, 走到睡覺區 enableOfTaskSleepByInterval:${this.enableOfTaskSleepByInterval} || ${restInInterval} ms`);
|
|
1077
|
-
}
|
|
1078
|
-
|
|
1079
|
-
// 當池正在運行時,將任務添加到執行隊列
|
|
1080
|
-
while (this.rulesOfAppendToExecutingTask()) {
|
|
1081
|
-
const taskInfo = this.getTaskInfoDependOnPriority();
|
|
1082
|
-
if (taskInfo) {
|
|
1083
|
-
const promise = this.taskWrapper(taskInfo.task, taskInfo.hash, this.queueOfWaitingParam.shift());
|
|
1084
|
-
|
|
1085
|
-
// 對於 RUN_BY_EACH_TASK,移除 TaskMap(任務不會重複執行)
|
|
1086
|
-
if (this.state === _configerer.configerer.POOLLER_STATE.RUN_BY_EACH_TASK) {
|
|
1087
|
-
this.removeTaskMapByHash(taskInfo.hash);
|
|
1088
|
-
}
|
|
1089
|
-
// 對於重複執行的任務,getTaskInfoDependOnPriority 已經生成新的 hash
|
|
1090
|
-
// 該 hash 會在任務完成時自動清理
|
|
1091
|
-
|
|
1092
|
-
this.appendTaskToExecuteQueue(taskInfo.hash, promise);
|
|
1093
|
-
} else {
|
|
1094
|
-
// 沒有 taskInfo,可能有未知的 issue,保險起見 break
|
|
1095
|
-
this.printLogMessage(`848451 也許有未知的issue,保險起見break`, true);
|
|
1096
|
-
break;
|
|
1097
|
-
}
|
|
1098
|
-
}
|
|
1099
|
-
this.printLogMessage(`4489844821, 離開了 syncTaskDispatcher()`);
|
|
1100
|
-
}
|
|
1101
|
-
/**
|
|
1102
|
-
* 檢查是否為等待結果的任務
|
|
1103
|
-
*
|
|
1104
|
-
* 如果有 callback function 就代表是一個需要回傳 result 的任務
|
|
1105
|
-
*
|
|
1106
|
-
* @param {string} hash - 任務的 hash
|
|
1107
|
-
* @returns {boolean} true 表示是等待結果的任務
|
|
1108
|
-
*/
|
|
1109
|
-
isWait4ResultTask(hash) {
|
|
1110
|
-
return this.mapOfHashNCallbackWrapper[hash] !== undefined;
|
|
1111
|
-
}
|
|
1112
|
-
}
|
|
1113
|
-
|
|
1114
|
-
// ============================================================================
|
|
1115
|
-
// 調試模式示例(僅在 DEBUG_MODE 時執行)
|
|
1116
|
-
// ============================================================================
|
|
1117
|
-
var _default = exports.default = InfinitePool;
|
|
1
|
+
"use strict";var _interopRequireDefault=require("@babel/runtime/helpers/interopRequireDefault");Object.defineProperty(exports,"__esModule",{value:!0}),exports.default=void 0;var _defineProperty2=_interopRequireDefault(require("@babel/runtime/helpers/defineProperty")),_lodash=_interopRequireDefault(require("lodash")),_index2=require("../index.js"),_configerer=require("configerer"),_exceptioner=_interopRequireDefault(require("../exceptioner"));function _classPrivateFieldInitSpec(e,t,s){_checkPrivateRedeclaration(e,t),t.set(e,s)}function _checkPrivateRedeclaration(e,t){if(t.has(e))throw new TypeError("Cannot initialize the same private elements twice on an object")}function _classPrivateFieldGet(e,t){return e.get(_assertClassBrand(e,t))}function _assertClassBrand(e,t,s){if("function"==typeof e?e===t:e.has(t))return arguments.length<3?t:s;throw new TypeError("Private element is not present on this object")}const SPECIFICITY_DEBUG=!1;var _run=new WeakMap;class InfinitePool{constructor(e=_configerer.configerer.POOLLER_WORKER_DEFAULT,t=_index2.utiller.getRandomValue(0,1e11)){(0,_defineProperty2.default)(this,"isRunInBackgroundMode",!1),(0,_defineProperty2.default)(this,"state",_configerer.configerer.POOLLER_STATE.RUN_BY_EACH_TASK),(0,_defineProperty2.default)(this,"enableOfTaskSleepByInterval",_configerer.configerer.POOLLER_ENABLE_TASK_SLEEP_BY_INTERVAL),(0,_defineProperty2.default)(this,"taskSleepInterval",_configerer.configerer.POOLLER_TASK_OF_INTERVAL_DEFAULT),(0,_defineProperty2.default)(this,"enableOfTaskTimeout",_configerer.configerer.POOLLER_ENABLE_TIMEOUT),(0,_defineProperty2.default)(this,"timeOfTaskTimeout",_configerer.configerer.POOLLER_TASK_TIMEOUT_DEFAULT),(0,_defineProperty2.default)(this,"handlerOfAssignTaskFail",void 0),(0,_defineProperty2.default)(this,"maximumOfWorker",void 0),(0,_defineProperty2.default)(this,"disableFirstRun",!1),(0,_defineProperty2.default)(this,"queueOfWaitingParam",[]),(0,_defineProperty2.default)(this,"countsOfRunByTimes",-1),(0,_defineProperty2.default)(this,"queueOfAssignTask",{}),(0,_defineProperty2.default)(this,"queueOfExecutingTask",[]),(0,_defineProperty2.default)(this,"isQueuePolling",!1),(0,_defineProperty2.default)(this,"initialTaskCompleted",!1),(0,_defineProperty2.default)(this,"mapOfHashNTask",{}),(0,_defineProperty2.default)(this,"mapOfHashNCallbackWrapper",{}),(0,_defineProperty2.default)(this,"nameOfCurrentPool",""),(0,_defineProperty2.default)(this,"atomicBgInstance",void 0),(0,_defineProperty2.default)(this,"setPoolId",(e=this.nameOfCurrentPool)=>{this.nameOfCurrentPool=e}),(0,_defineProperty2.default)(this,"getPoolId",()=>this.nameOfCurrentPool),(0,_defineProperty2.default)(this,"stopInBackground",async()=>{this.terminate();let e=0;for(;_lodash.default.size(this.queueOfExecutingTask)>0&&e<30;)await _index2.utiller.syncDelay(500),this.printLogMessage(`784512, 卡在 stopInBackground 出不來,${this.getLogMessageOfExecutingTaskQueueCount()}`),this.showState(),e++;return!(_lodash.default.size(this.queueOfExecutingTask)>0&&(this.printLogMessage(`stopInBackground 超時,仍有 ${_lodash.default.size(this.queueOfExecutingTask)} 個任務在執行`,!0),1))}),(0,_defineProperty2.default)(this,"isRunning",()=>this.isQueuePolling),(0,_defineProperty2.default)(this,"getCountOfAssignTaskInQueue",()=>{let e=0;for(const t of _configerer.configerer.POOLLER_PRIORITY)e+=this.queueOfAssignTask[t].length;return e}),(0,_defineProperty2.default)(this,"add",(e,t="low")=>{if("function"==typeof e){if(_configerer.configerer.POOLLER_PRIORITY.indexOf(t)<0)throw new _exceptioner.default(4001,`priority can't be ${t}`);const s=_index2.utiller.getRandomHash(),i={task:e,hash:s};return this.appendHashTaskMap(i),this.queueOfAssignTask[t].push(i),s}throw new _exceptioner.default(4002,"task can't be "+typeof e)}),(0,_defineProperty2.default)(this,"updateExecuteTaskState",e=>{const t=_lodash.default.find(this.queueOfExecutingTask,t=>_lodash.default.isEqual(t.hash,e));t&&(this.printLogMessage(`847875153, 客端委託的任務: ${e},更改狀態為 'ING'`),t.state="ING")}),(0,_defineProperty2.default)(this,"taskWrapper",(e,t,s)=>()=>{const i=this;let a,r,n="",o=!0;return new Promise((u,f)=>{i.enableOfTaskTimeout&&(n=setTimeout(()=>{try{throw this.printLogMessage(`982532, taskWrapper執行中,發生timeout: ${i.timeOfTaskTimeout} ms`),new _exceptioner.default(4010,i.getPoollerLogFormat(`TASK HASH:${t} IS TIMEOUT ${i.timeOfTaskTimeout} ms ${s?`,PARAMS IS ${JSON.stringify(s)}`:""}`))}catch(e){f(e)}},i.timeOfTaskTimeout)),this.printLogMessage(`984135, 客端委託的task開始執行 ${t}`),i.updateExecuteTaskState(t),e(s).then(e=>{this.printLogMessage(`984545, 客端委託的任務(TASK HASH:${t}),resolve回應: ${e}`),a=e,o=!0}).catch(e=>{this.printLogMessage(`989652, 客端委託的任務,reject回應: ${e.message}`,!0,e),r=e,o=!1}).finally(()=>{clearTimeout(n),u(),this.printLogMessage(`98942,(TASK HASH:${t}) taskWrapper()裡面第一個promise(為了timeout設計)完成了`)})}).then(()=>{if(o)return this.printLogMessage(`9894841,(TASK HASH:${t}) taskWrapper()裡面第二個promise(整個任務)完成了`),`${this.getLogMessageOfTaskHash(t)} completed`;throw r}).catch(e=>{if(o=!1,r=e,!i.isWait4ResultTask(t))if(void 0!==i.handlerOfAssignTaskFail)try{i.handlerOfAssignTaskFail(r)}catch(e){this.printLogMessage(`錯誤處理器執行失敗: ${e.message}`,!0,e)}else this.printLogMessage(`任務執行失敗但未設置錯誤處理器: ${r.message}`,!0,r)}).finally(()=>{const e={assignedTaskCompleted:o,resolve:a,reject:r};i.removeResolveOrRejectPromiseByHash(t,e),this.printLogMessage(`98943213, ${this.getLogMessageOfTaskHash(t)} taskWrapper()裡面第2個promise完成了`,!1,e)})}),(0,_defineProperty2.default)(this,"adds",(e,t="low")=>{const s=[];if(!_lodash.default.isArray(e))throw new _exceptioner.default(4003,"should be async function array, not "+typeof e);for(const i of e)s.push(this.add(i,t));return s}),(0,_defineProperty2.default)(this,"removeTaskMapByHash",e=>{delete this.mapOfHashNTask[e]}),(0,_defineProperty2.default)(this,"beforeRun",()=>{this.isQueuePolling=!0}),(0,_defineProperty2.default)(this,"afterRun",()=>{this.clearCache()}),(0,_defineProperty2.default)(this,"runInInfinite",async(e=[],t)=>{if(this.beforeRun(),_lodash.default.isFunction(e))this.add(e);else{if(!_lodash.default.isArray(e))throw new _exceptioner.default(4006,"type of task is ===> "+typeof e);this.adds(e)}for(this.enableTaskSleepInterval(_lodash.default.isNumber(t),t),this.setState(_configerer.configerer.POOLLER_STATE.RUN_INFINITE);!this.ruleOfStopInfiniteRun();)this.printLogMessage(`415123, runInInfinite() 正在無限Loop中, ${this.getLogMessageOfExecutingTaskQueueCount()}`),await _classPrivateFieldGet(_run,this).call(this)}),(0,_defineProperty2.default)(this,"ruleOfStopInfiniteRun",()=>!this.isRunning()&&this.isExecutingTaskQueueEmpty()),(0,_defineProperty2.default)(this,"isExecutingTaskQueueEmpty",()=>0===_lodash.default.size(this.queueOfExecutingTask)),(0,_defineProperty2.default)(this,"appendParamInToQueue",(...e)=>{this.triggerBgInstance(),this.queueOfWaitingParam.push(...e)}),(0,_defineProperty2.default)(this,"runByParams",async(e,...t)=>{if(void 0===e&&(e=this.queueOfAssignTask.low.shift().task),!_lodash.default.isFunction(e))throw new _exceptioner.default(4006,"runByParams error, typeof task can't be "+typeof e);if(!_lodash.default.isArray(t))throw new _exceptioner.default(4006,"runByParams error, typeof params can't be "+typeof t);for(this.beforeRun(),this.add(e),this.appendParamInToQueue(...t),this.setState(_configerer.configerer.POOLLER_STATE.RUN_BY_PARAMS);_lodash.default.size(this.queueOfWaitingParam)>0;)await _classPrivateFieldGet(_run,this).call(this);for(;!this.isExecutingTaskQueueEmpty();)await _index2.utiller.syncDelay(100),this.printLogMessage(`等待執行中的任務完成,剩餘: ${_lodash.default.size(this.queueOfExecutingTask)}`);this.printLogMessage("951281952, runByParams() 結束了while()"),this.terminate()}),(0,_defineProperty2.default)(this,"runByEachTask",async(e=[])=>{for(this.id=_index2.utiller.getRandomHash(15),this.beforeRun(),this.adds(e),this.setState(_configerer.configerer.POOLLER_STATE.RUN_BY_EACH_TASK);!this.ruleOfStopInfiniteRun();)await _classPrivateFieldGet(_run,this).call(this,this.id),this.getCountOfAssignTaskInQueue()<=0&&(this.terminate(),this.printLogMessage("788121, runByEachTask() 因為 taskOfWaitingQueue 清空而停止")),await _index2.utiller.syncDelay(10),this.printLogMessage("788143, runByEachTask() 為了讓while不要停止運算");this.printLogMessage("7881952, runByEachTask() 結束了while()")}),(0,_defineProperty2.default)(this,"runByTimes",async(e,t=1)=>{for(1!==this.maximumOfWorker&&this.printLogMessage(`警告: runByTimes 模式建議使用 1 個 worker,當前為 ${this.maximumOfWorker}`,!0),this.countsOfRunByTimes=t,this.add(e),this.beforeRun(),this.setState(_configerer.configerer.POOLLER_STATE.RUN_BY_TIMES);!this.ruleOfStopInfiniteRun()&&this.countsOfRunByTimes>0;)await _classPrivateFieldGet(_run,this).call(this)}),(0,_defineProperty2.default)(this,"runInBackGround",(e,...t)=>{if(this.isRunInBackgroundMode=!0,"function"!=typeof e)throw new _exceptioner.default(4002,"_asyncfunc can't be "+typeof e);return setTimeout(async()=>{try{await e.apply(this,t)}catch(e){if(!(e instanceof _exceptioner.default))throw new _exceptioner.default(4009,{message:`${this.getPoollerLogFormat("")}`},e);this.printLogMessage(`7812123, runInBackGround() 執行錯誤: ${e.message}`,!0,e)}finally{this.terminate(),this.printLogMessage("7812123, runInBackGround() 走到finally")}},1)}),(0,_defineProperty2.default)(this,"getPoollerLogFormat",e=>`POOLLER NAME: ${this.getPoolId()}${_lodash.default.isEmpty(e)?"":" , "}${e}`),(0,_defineProperty2.default)(this,"setTaskFailHandler",(e=e=>{})=>{this.handlerOfAssignTaskFail=e}),(0,_defineProperty2.default)(this,"rulesOfAppendToExecutingTask",()=>{switch(this.state){case _configerer.configerer.POOLLER_STATE.RUN_BY_EACH_TASK:return this.isRunning()&&!this.isExecutingQueueFull()&&this.getCountOfAssignTaskInQueue()>0;case _configerer.configerer.POOLLER_STATE.RUN_BY_PARAMS:return this.isRunning()&&!this.isExecutingQueueFull()&&_lodash.default.size(this.queueOfWaitingParam)>0;case _configerer.configerer.POOLLER_STATE.RUN_BY_TIMES:case _configerer.configerer.POOLLER_STATE.RUN_INFINITE:return this.isRunning()&&!this.isExecutingQueueFull()&&this.getCountOfAssignTaskInQueue()>0;default:throw new _exceptioner.default(4005,`this.state ==> ${this.state}`)}}),(0,_defineProperty2.default)(this,"appendTaskToExecuteQueue",(e,t)=>{_lodash.default.isEqual(this.state,_configerer.configerer.POOLLER_STATE.RUN_BY_TIMES)&&(this.countsOfRunByTimes=this.countsOfRunByTimes-1);const s={state:"NOT",hash:e,task:t};this.printLogMessage(`4484451, 增加了一個assignedTask ${this.getLogMessageOfTaskHash(e)} 到 QueueOfExecutingTask ,${this.getLogMessageOfExecutingTaskQueueCount()}`,!1,s),this.queueOfExecutingTask.push(s)}),(0,_defineProperty2.default)(this,"getLogMessageOfExecutingTaskQueueCount",()=>`ExecutingTaskQueueCount: ${_lodash.default.size(this.queueOfExecutingTask)}`),(0,_defineProperty2.default)(this,"getLogMessageOfAssignTaskQueueCount",()=>`AssignTaskQueueCount: ${this.getCountOfAssignTaskInQueue()}`),(0,_defineProperty2.default)(this,"getLogMessageOfTaskHash",e=>`TASK HASH: ${e}`),(0,_defineProperty2.default)(this,"showState",()=>{_index2.utiller.appendInfo(this.getPoollerLogFormat(`workerCount: ${this.maximumOfWorker}`)),_index2.utiller.appendInfo(this.getPoollerLogFormat(`taskQueue(還在排隊的Task): ${this.getCountOfAssignTaskInQueue()}`)),_index2.utiller.appendInfo(this.getPoollerLogFormat(`QueueOfExecutingTask(正在執行的AsyncTask, 超過workerCount就是bug): ${_lodash.default.size(this.queueOfExecutingTask)}`)),_index2.utiller.appendInfo(this.getPoollerLogFormat(`mapOfHashNTask(還沒執行到的AsyncTask reference的暫存區): ${_lodash.default.size(this.mapOfHashNTask)}`))}),_classPrivateFieldInitSpec(this,_run,async()=>{const e=this;if(await this.syncTaskDispatcher(),0===this.maximumOfWorker)await async function(){e.printLogMessage("因為max count of worker為0,所以指派一個簡單的任務"),await _index2.utiller.syncDelay(10)}();else if(this.isExecutingTaskQueueEmpty())this.printLogMessage("4574152 不應該走到這裏,但是 minor issue",!0);else{this.printLogMessage(`4512211, 開始任務(taskWrapper): ${this.getLogMessageOfExecutingTaskQueueCount()}`);const t=await async function(){const t=_lodash.default.filter(e.queueOfExecutingTask,e=>_lodash.default.isEqual(e.state,"NOT")).map(e=>(0,e.task)());e.printLogMessage(`454652321, 開始任務(taskWrapper): run() 裡面的execute開始執行, task(state = NOT)的長度 ${_lodash.default.size(t)}`);const s=_lodash.default.size(t)>0?await Promise.race(t):"4542131684, task is empty";return e.printLogMessage("54121445161, 結束任務(taskWrapper): run() 裡面的execute結束執行"),s}();this.printLogMessage(`4512213 完畢任務(taskWrapper:${t}), ${this.getLogMessageOfExecutingTaskQueueCount()}, ${this.getLogMessageOfAssignTaskQueueCount()}`)}this.queueOfExecutingTask.length>this.maximumOfWorker&&this.printLogMessage(`4512214 一定是改壞了!!!!!!!!!!, ${this.getLogMessageOfExecutingTaskQueueCount} `,!0),e.printLogMessage("5478421212, 離開 run()")}),(0,_defineProperty2.default)(this,"getTaskInfoForRepetitiveRun",e=>{const t={task:e.task,hash:_index2.utiller.getRandomHash()};return this.appendHashTaskMap(t),t}),(0,_defineProperty2.default)(this,"getTaskInfoDependOnPriority",()=>{for(const e of _configerer.configerer.POOLLER_PRIORITY)if(this.queueOfAssignTask[e].length>0)switch(this.state){case _configerer.configerer.POOLLER_STATE.RUN_BY_EACH_TASK:return this.queueOfAssignTask[e].shift();case _configerer.configerer.POOLLER_STATE.RUN_BY_PARAMS:case _configerer.configerer.POOLLER_STATE.RUN_BY_TIMES:case _configerer.configerer.POOLLER_STATE.RUN_INFINITE:const t=this.queueOfAssignTask[e][0];return this.getTaskInfoForRepetitiveRun(t);default:throw new _exceptioner.default(4005,`this.state ==> ${this.state}`)}if(!_lodash.default.isEqual(this.state,_configerer.configerer.POOLLER_STATE.RUN_BY_EACH_TASK))throw new _exceptioner.default(4007)}),(0,_defineProperty2.default)(this,"removeResolveOrRejectPromiseByHash",(e,t)=>{const s=this.mapOfHashNCallbackWrapper[e];if(void 0!==s){this.printLogMessage(`5644153248, removeResolveOrRejectPromiseByHash 拿掉了完成的任務(${this.getLogMessageOfTaskHash(e)})`);try{s(t)}catch(e){this.printLogMessage(`回調執行失敗: ${e.message}`,!0,e)}finally{delete this.mapOfHashNCallbackWrapper[e]}}this.removePromiseFromExecutingQueue(e)}),(0,_defineProperty2.default)(this,"removePromiseFromExecutingQueue",e=>{this.printLogMessage(`56448412, QueueOfExecutingTask 拿掉了完成的任務 ${this.getLogMessageOfTaskHash(e)}`),_lodash.default.remove(this.queueOfExecutingTask,t=>_lodash.default.isEqual(e,t.hash))}),(0,_defineProperty2.default)(this,"runInfiniteInBackground",(e,t)=>this.invokeInstanceOfBackground(this.runInInfinite,e,t)),(0,_defineProperty2.default)(this,"runByParamInBackGround",(e,...t)=>this.invokeInstanceOfBackground(this.runByParams,e,...t)),(0,_defineProperty2.default)(this,"runByTimesInBackGround",(e,t)=>this.invokeInstanceOfBackground(this.runByTimes,e,t)),(0,_defineProperty2.default)(this,"runByEachTaskInBackGround",(...e)=>this.invokeInstanceOfBackground(this.runByEachTask,...e)),(0,_defineProperty2.default)(this,"invokeInstanceOfBackground",(e,...t)=>(void 0!==this.atomicBgInstance&&clearTimeout(this.atomicBgInstance),this.atomicBgInstance=this.runInBackGround(e,...t),this)),this.maximumOfWorker=e,this.setPoolId(_lodash.default.toString(t));for(const e of _configerer.configerer.POOLLER_PRIORITY)this.queueOfAssignTask[e]=[]}enableQueueTerminateBySleepCount(e=!0,t=_configerer.configerer.POOLLER_QUEUE_TIME_OF_SLEEP_INTERVAL_DEFAULT,s=_configerer.configerer.POOLLER_QUEUE_MAX_SLEEP_COUNTS_DEFAULT){this.enableOfQueueTerminateSleepCount=e,this.queueMaxSleepCounts=s,this.intervalOfQueueSleep=t}clearCache(){this.queueOfExecutingTask.length=0,this.mapOfHashNTask={},this.queueOfAssignTask={}}terminate(){this.isQueuePolling=!1}printLogMessage(e,t=!1,...s){}setWorker(e){this.maximumOfWorker=e}cleanTaskInterval(){this.taskSleepInterval={min:0,max:0}}enableTaskSleepInterval(e=!0,t=_configerer.configerer.POOLLER_TASK_OF_INTERVAL_DEFAULT){this.enableOfTaskSleepByInterval=e,_lodash.default.isNumber(t)&&(t={min:t,max:t}),this.taskSleepInterval=t}enableTaskTimeout(e=!0,t=_configerer.configerer.POOLLER_TASK_TIMEOUT_DEFAULT){this.enableOfTaskTimeout=e,this.timeOfTaskTimeout=t}async addTaskAndWait4Result(e,t="low",s="noName"){return this.triggerBgInstance(),new Promise((s,i)=>{const a=this.add(e,t);this.registerHash4Result(a,e=>{e.assignedTaskCompleted?s(e.resolve):i(e.reject)})})}registerHash4Result(e,t){this.mapOfHashNCallbackWrapper[e]=t}appendHashTaskMap(e){this.mapOfHashNTask[e.hash]=e}getTaskInfoByHash(e){return this.mapOfHashNTask[e]}remove(e){let t=this.getTaskInfoByHash(e);if(t){for(const s of _configerer.configerer.POOLLER_PRIORITY){const i=_lodash.default.indexOf(this.queueOfAssignTask[s],t);if(i>=0)return this.queueOfAssignTask[s].splice(i,1),this.removeTaskMapByHash(e),!0}return!1}throw new _exceptioner.default(4004,e)}setState(e){this.state=e}checkAndMarkInitialTaskStatus(){return this.initialTaskCompleted?this.initialTaskCompleted:(this.initialTaskCompleted=!0,!1)}setDisableFirstRun(e=!0){this.disableFirstRun=e}triggerBgInstance(){if(this.isRunInBackgroundMode&&!this.isQueuePolling)if(this.state!==_configerer.configerer.POOLLER_STATE.RUN_BY_EACH_TASK){if(this.state!==_configerer.configerer.POOLLER_STATE.RUN_BY_PARAMS)throw new _exceptioner.default(4011,`this.state is ==> ${_index2.utiller.getItsKeyByValue(_configerer.configerer.POOLLER_STATE,this.state)}`);this.runByParamInBackGround()}else this.runByEachTaskInBackGround()}isExecutingQueueFull(){return _lodash.default.size(this.queueOfExecutingTask)>=this.maximumOfWorker}isTaskQueueEmpty(){return 0===this.getCountOfAssignTaskInQueue()}async syncTaskDispatcher(){this.printLogMessage("448984466, 走進來了 syncTaskDispatcher()");const e=this.disableFirstRun&&!this.checkAndMarkInitialTaskStatus(),t=this.queueOfExecutingTask.length>=this.maximumOfWorker-1,s=this.checkAndMarkInitialTaskStatus()&&t&&this.enableOfTaskSleepByInterval;if(e||s){const e=await _index2.utiller.syncDelayRandom(this.taskSleepInterval.min,this.taskSleepInterval.max);this.printLogMessage(`4484121, 走到睡覺區 enableOfTaskSleepByInterval:${this.enableOfTaskSleepByInterval} || ${e} ms`)}for(;this.rulesOfAppendToExecutingTask();){const e=this.getTaskInfoDependOnPriority();if(!e){this.printLogMessage("848451 也許有未知的issue,保險起見break",!0);break}{const t=this.taskWrapper(e.task,e.hash,this.queueOfWaitingParam.shift());this.state===_configerer.configerer.POOLLER_STATE.RUN_BY_EACH_TASK&&this.removeTaskMapByHash(e.hash),this.appendTaskToExecuteQueue(e.hash,t)}}this.printLogMessage("4489844821, 離開了 syncTaskDispatcher()")}isWait4ResultTask(e){return void 0!==this.mapOfHashNCallbackWrapper[e]}}var _default=exports.default=InfinitePool;
|