@ives_xxz/framework 2.1.23 → 2.1.24

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.
@@ -13,170 +13,228 @@ export default class FWPromiseManager extends FWManager implements FW.PromiseMan
13
13
  this.promiseRegistry.clear();
14
14
  }
15
15
 
16
- /** 创建Promise执行器 */
17
16
  public execute<T = any>(
18
17
  executor: FW.PromiseExcutor<T>,
19
18
  options: FW.PromiseExecuteOptions = {},
20
19
  ): FW.PromiseProxy<T> {
21
20
  const id = this.uniqueId++;
22
- const abortController = new AbortController();
23
21
  const maxRetryTimes = options.retryCount || 0;
24
22
  const retryInterval = options.retryInterval || 0;
25
23
  let retryCount = 0;
26
24
  let timerSchedule: FW.TimerSchedule;
25
+ let cancelResolve: (() => void) | null = null;
26
+ let isCancelled = false;
27
+ let abortListeners: Array<(this: any, ev: Event) => any> = [];
27
28
 
28
29
  const createPromise = (): Promise<T> => {
29
30
  return new Promise<T>((resolve, reject) => {
31
+ let cancelPromiseResolve: (() => void) | null = null;
32
+ const cancelPromise = new Promise<never>((resolve, reject) => {
33
+ cancelResolve = () => {
34
+ isCancelled = true;
35
+ if (cancelPromiseResolve) {
36
+ cancelPromiseResolve();
37
+ } else {
38
+ FW.Entry.timeMgr.scheduleOnce(() => {
39
+ if (cancelPromiseResolve) cancelPromiseResolve();
40
+ });
41
+ }
42
+ };
43
+ }).then(() => {
44
+ return new Promise<never>(() => {});
45
+ });
46
+
47
+ const mainPromise = new Promise<T>((innerResolve, innerReject) => {
48
+ try {
49
+ const signal = {
50
+ aborted: isCancelled,
51
+ onabort: null as ((this: any, ev: Event) => any) | null,
52
+ addEventListener: (type: string, listener: (this: any, ev: Event) => any) => {
53
+ if (type === 'abort') {
54
+ signal.onabort = listener;
55
+ }
56
+ },
57
+ removeEventListener: () => {
58
+ signal.onabort = null;
59
+ },
60
+ };
61
+
62
+ executor(
63
+ (value) => {
64
+ if (!isCancelled) innerResolve(value);
65
+ },
66
+ (reason) => {
67
+ if (!isCancelled) innerReject(reason);
68
+ },
69
+ signal as any,
70
+ options.reason,
71
+ );
72
+
73
+ cancelPromiseResolve = () => {
74
+ if (signal.onabort) {
75
+ signal.onabort.call(signal, { type: 'abort' } as Event);
76
+ }
77
+ abortListeners.forEach((listener) => {
78
+ try {
79
+ listener.call({ type: 'abort' }, { type: 'abort' } as Event);
80
+ } catch (error) {
81
+ FW.Log.error('Error in abort listener:', error);
82
+ }
83
+ });
84
+ };
85
+ } catch (error) {
86
+ if (!isCancelled) innerReject(error);
87
+ }
88
+ });
89
+
90
+ let timeoutPromise: Promise<T> | null = null;
30
91
  if (options.timeout && options.timeout > 0) {
31
- timerSchedule = FW.Entry.timeMgr.scheduleOnce(() => {
32
- const timeoutError = new Error(`Promise ${id} timeout after ${options.timeout} s`);
33
- if (
34
- retryCount < maxRetryTimes &&
35
- (!options.retryCondition || options.retryCondition(timeoutError, retryCount))
36
- ) {
37
- retryCount++;
38
- FW.Log.debug(`Promise ${id} timeout, retrying (${retryCount}/${maxRetryTimes})`);
39
- if (retryInterval > 0) {
40
- FW.Entry.timeMgr.scheduleOnce(() => {
92
+ timeoutPromise = new Promise<T>((_, timeoutReject) => {
93
+ timerSchedule = FW.Entry.timeMgr.scheduleOnce(() => {
94
+ const timeoutError = new Error(`Promise ${id} timeout after ${options.timeout} s`);
95
+
96
+ if (
97
+ retryCount < maxRetryTimes &&
98
+ (!options.retryCondition || options.retryCondition(timeoutError, retryCount))
99
+ ) {
100
+ retryCount++;
101
+ FW.Log.debug(`Promise ${id} timeout, retrying (${retryCount}/${maxRetryTimes})`);
102
+
103
+ if (retryInterval > 0) {
104
+ FW.Entry.timeMgr.scheduleOnce(() => {
105
+ createPromise().then(resolve, reject);
106
+ }, retryInterval);
107
+ } else {
41
108
  createPromise().then(resolve, reject);
42
- }, retryInterval);
109
+ }
43
110
  } else {
44
- createPromise().then(resolve, reject);
111
+ timeoutReject(timeoutError);
45
112
  }
46
- } else {
47
- abortController.abort(timeoutError.message);
48
- timerSchedule?.unSchedule();
49
- }
50
- }, options.timeout);
113
+ }, options.timeout);
114
+ });
51
115
  }
52
116
 
53
- const onAbort = () => {
54
- FW.Log.debug('promise abort');
55
- timerSchedule?.unSchedule();
56
- this.removePromise(id);
57
- };
117
+ const racePromises: Promise<T>[] = [mainPromise];
58
118
 
59
- if (abortController.signal.aborted) {
60
- onAbort();
61
- return;
119
+ if (timeoutPromise) {
120
+ racePromises.push(timeoutPromise);
62
121
  }
63
122
 
64
- abortController.signal.addEventListener('abort', onAbort);
123
+ if (!isCancelled) {
124
+ const raceCancelPromise = cancelPromise as unknown as Promise<T>;
125
+ racePromises.push(raceCancelPromise);
126
+ }
65
127
 
66
- const wrappedResolve = (value: T | PromiseLike<T>) => {
67
- abortController.signal.removeEventListener('abort', onAbort);
68
- this.removePromise(id);
69
- timerSchedule?.unSchedule();
70
- resolve(value);
71
- };
128
+ Promise.race(racePromises)
129
+ .then((value) => {
130
+ if (isCancelled) return;
131
+ timerSchedule?.unSchedule();
132
+ resolve(value);
133
+ this.removePromise(id);
134
+ })
135
+ .catch((error) => {
136
+ if (isCancelled) return;
72
137
 
73
- const wrappedReject = (reason?: any) => {
74
- timerSchedule?.unSchedule();
75
- if (
76
- retryCount < maxRetryTimes &&
77
- (!options.retryCondition || options.retryCondition(reason, retryCount))
78
- ) {
79
- retryCount++;
80
- FW.Log.debug(
81
- `Promise ${id} failed, retrying (${retryCount}/${maxRetryTimes}):`,
82
- reason,
83
- );
84
- if (retryInterval > 0) {
85
- FW.Entry.timeMgr.scheduleOnce(() => {
138
+ timerSchedule?.unSchedule();
139
+
140
+ if (
141
+ retryCount < maxRetryTimes &&
142
+ (!options.retryCondition || options.retryCondition(error, retryCount))
143
+ ) {
144
+ retryCount++;
145
+ FW.Log.debug(
146
+ `Promise ${id} failed, retrying (${retryCount}/${maxRetryTimes}):`,
147
+ error,
148
+ );
149
+
150
+ if (retryInterval > 0) {
151
+ FW.Entry.timeMgr.scheduleOnce(() => {
152
+ createPromise().then(resolve, reject);
153
+ }, retryInterval);
154
+ } else {
86
155
  createPromise().then(resolve, reject);
87
- }, retryInterval);
156
+ }
88
157
  } else {
89
- createPromise().then(resolve, reject);
158
+ reject(error);
159
+ this.removePromise(id);
90
160
  }
91
- } else {
92
- abortController.signal.removeEventListener('abort', onAbort);
93
- this.removePromise(id);
94
- reject(reason);
95
- }
96
- };
97
- try {
98
- executor(wrappedResolve, wrappedReject, abortController.signal, options.reason);
99
- } catch (error) {
100
- wrappedReject(error);
101
- }
161
+ });
102
162
  });
103
163
  };
164
+
104
165
  const promise = createPromise();
105
166
  const promiseProxy: FW.PromiseProxy<T> = {
106
167
  id,
107
168
  promise,
108
169
  status: FW.SystemDefine.FWPromiseStatus.PENDING,
109
- abortController,
110
170
  abort: (reason?: any) => {
111
171
  if (promiseProxy.status === FW.SystemDefine.FWPromiseStatus.PENDING) {
112
172
  FW.Log.debug(reason || 'promise cancelled');
113
- abortController.abort(reason);
173
+ cancelResolve?.();
174
+ promiseProxy.status = FW.SystemDefine.FWPromiseStatus.CANCELLED;
114
175
  }
115
176
  },
116
-
117
177
  addAbortEventListener: (
118
- listener: (this: AbortSignal, ev: Event) => any,
178
+ listener: (this: any, ev: Event) => any,
119
179
  options?: boolean | AddEventListenerOptions,
120
180
  ) => {
121
- abortController.signal.addEventListener('abort', listener, options);
181
+ abortListeners.push(listener);
182
+ return () => {
183
+ const index = abortListeners.indexOf(listener);
184
+ if (index > -1) {
185
+ abortListeners.splice(index, 1);
186
+ }
187
+ };
122
188
  },
123
189
  };
190
+
124
191
  this.promiseRegistry.set(id, promiseProxy);
192
+
193
+ promise
194
+ .then(() => {
195
+ if (promiseProxy.status === FW.SystemDefine.FWPromiseStatus.PENDING) {
196
+ promiseProxy.status = FW.SystemDefine.FWPromiseStatus.FULFILLED;
197
+ }
198
+ })
199
+ .catch((error) => {
200
+ if (promiseProxy.status === FW.SystemDefine.FWPromiseStatus.PENDING) {
201
+ promiseProxy.status = FW.SystemDefine.FWPromiseStatus.REJECTED;
202
+ }
203
+ });
204
+
125
205
  return promiseProxy;
126
206
  }
127
207
 
128
- /** 批量执行Promise并等待所有完成 */
129
208
  public all<T = any>(
130
209
  promises: FW.PromiseProxy<T>[],
131
210
  options: FW.PromiseExecuteOptions = {},
132
211
  ): FW.PromiseProxy<FW.PromiseAllResult<T>> {
133
212
  const id = this.uniqueId++;
134
- const abortController = new AbortController();
135
213
  const maxRetryTimes = options.retryCount || 0;
136
214
  const retryInterval = options.retryInterval || 0;
137
215
  let timerSchedule: FW.TimerSchedule;
138
216
  let retryCount = 0;
217
+ let cancelResolve: (() => void) | null = null;
218
+ let isCancelled = false;
219
+ let abortListeners: Array<(this: any, ev: Event) => any> = [];
139
220
 
140
221
  const createPromise = (): Promise<FW.PromiseAllResult<T>> => {
141
222
  return new Promise<FW.PromiseAllResult<T>>((resolve, reject) => {
142
- if (options.timeout && options.timeout > 0) {
143
- timerSchedule?.unSchedule();
144
- timerSchedule = FW.Entry.timeMgr.scheduleOnce(() => {
145
- const timeoutError = new Error(`All Promise ${id} timeout after ${options.timeout} s`);
146
- if (
147
- retryCount < maxRetryTimes &&
148
- (!options.retryCondition || options.retryCondition(timeoutError, retryCount))
149
- ) {
150
- retryCount++;
151
- FW.Log.debug(`All Promise ${id} timeout, retrying (${retryCount}/${maxRetryTimes})`);
152
- if (retryInterval > 0) {
153
- FW.Entry.timeMgr.scheduleOnce(() => {
154
- createPromise().then(resolve, reject);
155
- }, retryInterval);
156
- } else {
157
- createPromise().then(resolve, reject);
158
- }
223
+ let cancelPromiseResolve: (() => void) | null = null;
224
+ const cancelPromise = new Promise<never>((_, rejectCancel) => {
225
+ cancelResolve = () => {
226
+ isCancelled = true;
227
+ if (cancelPromiseResolve) {
228
+ cancelPromiseResolve();
159
229
  } else {
160
- abortController.abort(timeoutError.message);
161
- timerSchedule?.unSchedule();
230
+ setTimeout(() => {
231
+ if (cancelPromiseResolve) cancelPromiseResolve();
232
+ }, 0);
162
233
  }
163
- }, options.timeout);
164
- }
165
-
166
- const onAbort = () => {
167
- timerSchedule?.unSchedule();
168
- if (promiseProxy.status === FW.SystemDefine.FWPromiseStatus.PENDING) {
169
- promiseProxy.status = FW.SystemDefine.FWPromiseStatus.CANCELLED;
170
- this.removePromise(id);
171
- }
172
- };
173
-
174
- if (abortController.signal.aborted) {
175
- onAbort();
176
- return;
177
- }
178
-
179
- abortController.signal.addEventListener('abort', onAbort);
234
+ };
235
+ }).then(() => {
236
+ return new Promise<never>(() => {});
237
+ });
180
238
 
181
239
  const processAll = async (): Promise<FW.PromiseAllResult<T>> => {
182
240
  const result: FW.PromiseAllResult<T> = {
@@ -186,8 +244,7 @@ export default class FWPromiseManager extends FWManager implements FW.PromiseMan
186
244
  };
187
245
 
188
246
  for (const promiseProxy of promises) {
189
- if (abortController.signal.aborted) {
190
- // 找出所有尚未处理的任务标记为取消
247
+ if (isCancelled) {
191
248
  const remainingIds = promises
192
249
  .filter(
193
250
  (p) =>
@@ -220,16 +277,72 @@ export default class FWPromiseManager extends FWManager implements FW.PromiseMan
220
277
  return result;
221
278
  };
222
279
 
223
- processAll()
280
+ const mainPromise = processAll();
281
+
282
+ cancelPromiseResolve = () => {
283
+ abortListeners.forEach((listener) => {
284
+ try {
285
+ listener.call({ type: 'abort' }, { type: 'abort' } as Event);
286
+ } catch (error) {
287
+ FW.Log.error('Error in abort listener:', error);
288
+ }
289
+ });
290
+ };
291
+
292
+ let timeoutPromise: Promise<FW.PromiseAllResult<T>> | null = null;
293
+ if (options.timeout && options.timeout > 0) {
294
+ timeoutPromise = new Promise<FW.PromiseAllResult<T>>((_, timeoutReject) => {
295
+ timerSchedule = FW.Entry.timeMgr.scheduleOnce(() => {
296
+ const timeoutError = new Error(
297
+ `All Promise ${id} timeout after ${options.timeout} s`,
298
+ );
299
+
300
+ if (
301
+ retryCount < maxRetryTimes &&
302
+ (!options.retryCondition || options.retryCondition(timeoutError, retryCount))
303
+ ) {
304
+ retryCount++;
305
+ FW.Log.debug(
306
+ `All Promise ${id} timeout, retrying (${retryCount}/${maxRetryTimes})`,
307
+ );
308
+
309
+ if (retryInterval > 0) {
310
+ FW.Entry.timeMgr.scheduleOnce(() => {
311
+ createPromise().then(resolve, reject);
312
+ }, retryInterval);
313
+ } else {
314
+ createPromise().then(resolve, reject);
315
+ }
316
+ } else {
317
+ timeoutReject(timeoutError);
318
+ }
319
+ }, options.timeout);
320
+ });
321
+ }
322
+
323
+ const racePromises: Promise<FW.PromiseAllResult<T>>[] = [mainPromise];
324
+
325
+ if (timeoutPromise) {
326
+ racePromises.push(timeoutPromise);
327
+ }
328
+
329
+ if (!isCancelled) {
330
+ const raceCancelPromise = cancelPromise as unknown as Promise<FW.PromiseAllResult<T>>;
331
+ racePromises.push(raceCancelPromise);
332
+ }
333
+
334
+ Promise.race(racePromises)
224
335
  .then((result) => {
225
- promiseProxy.status = FW.SystemDefine.FWPromiseStatus.FULFILLED;
226
- abortController.signal.removeEventListener('abort', onAbort);
227
- this.removePromise(id);
336
+ if (isCancelled) return;
228
337
  timerSchedule?.unSchedule();
229
338
  resolve(result);
339
+ this.removePromise(id);
230
340
  })
231
341
  .catch((error) => {
342
+ if (isCancelled) return;
343
+
232
344
  timerSchedule?.unSchedule();
345
+
233
346
  if (
234
347
  retryCount < maxRetryTimes &&
235
348
  (!options.retryCondition || options.retryCondition(error, retryCount))
@@ -239,6 +352,7 @@ export default class FWPromiseManager extends FWManager implements FW.PromiseMan
239
352
  `All Promise ${id} failed, retrying (${retryCount}/${maxRetryTimes}):`,
240
353
  error,
241
354
  );
355
+
242
356
  if (retryInterval > 0) {
243
357
  FW.Entry.timeMgr.scheduleOnce(() => {
244
358
  createPromise().then(resolve, reject);
@@ -247,12 +361,8 @@ export default class FWPromiseManager extends FWManager implements FW.PromiseMan
247
361
  createPromise().then(resolve, reject);
248
362
  }
249
363
  } else {
250
- if (promiseProxy.status === FW.SystemDefine.FWPromiseStatus.PENDING) {
251
- promiseProxy.status = FW.SystemDefine.FWPromiseStatus.REJECTED;
252
- abortController.signal.removeEventListener('abort', onAbort);
253
- this.removePromise(id);
254
- reject(error);
255
- }
364
+ reject(error);
365
+ this.removePromise(id);
256
366
  }
257
367
  });
258
368
  });
@@ -263,26 +373,44 @@ export default class FWPromiseManager extends FWManager implements FW.PromiseMan
263
373
  id,
264
374
  promise,
265
375
  status: FW.SystemDefine.FWPromiseStatus.PENDING,
266
- abortController,
267
376
  abort: (reason?: any) => {
268
377
  if (promiseProxy.status === FW.SystemDefine.FWPromiseStatus.PENDING) {
269
378
  FW.Log.debug(reason || 'all promise cancelled');
270
- abortController.abort(reason);
379
+ cancelResolve?.();
380
+ promiseProxy.status = FW.SystemDefine.FWPromiseStatus.CANCELLED;
271
381
  }
272
382
  },
273
383
  addAbortEventListener: (
274
- listener: (this: AbortSignal, ev: Event) => any,
384
+ listener: (this: any, ev: Event) => any,
275
385
  options?: boolean | AddEventListenerOptions,
276
386
  ) => {
277
- abortController.signal.addEventListener('abort', listener, options);
387
+ abortListeners.push(listener);
388
+ return () => {
389
+ const index = abortListeners.indexOf(listener);
390
+ if (index > -1) {
391
+ abortListeners.splice(index, 1);
392
+ }
393
+ };
278
394
  },
279
395
  };
280
396
 
281
397
  this.promiseRegistry.set(id, promiseProxy);
398
+
399
+ promise
400
+ .then(() => {
401
+ if (promiseProxy.status === FW.SystemDefine.FWPromiseStatus.PENDING) {
402
+ promiseProxy.status = FW.SystemDefine.FWPromiseStatus.FULFILLED;
403
+ }
404
+ })
405
+ .catch((error) => {
406
+ if (promiseProxy.status === FW.SystemDefine.FWPromiseStatus.PENDING) {
407
+ promiseProxy.status = FW.SystemDefine.FWPromiseStatus.REJECTED;
408
+ }
409
+ });
410
+
282
411
  return promiseProxy;
283
412
  }
284
413
 
285
- /** 取消指定Promise */
286
414
  public cancel(id: number, reason?: any): boolean {
287
415
  const promiseProxy = this.promiseRegistry.get(id);
288
416
  if (promiseProxy && promiseProxy.status === FW.SystemDefine.FWPromiseStatus.PENDING) {
@@ -292,7 +420,6 @@ export default class FWPromiseManager extends FWManager implements FW.PromiseMan
292
420
  return false;
293
421
  }
294
422
 
295
- /** 批量取消Promise */
296
423
  public cancelMultiple(ids: number[], reason?: any): number[] {
297
424
  const cancelled: number[] = [];
298
425
  ids.forEach((id) => {
@@ -303,7 +430,6 @@ export default class FWPromiseManager extends FWManager implements FW.PromiseMan
303
430
  return cancelled;
304
431
  }
305
432
 
306
- /** 取消所有Promise */
307
433
  public cancelAll(reason?: any): number[] {
308
434
  const cancelled: number[] = [];
309
435
  this.promiseRegistry.forEach((promiseProxy, id) => {
@@ -313,13 +439,11 @@ export default class FWPromiseManager extends FWManager implements FW.PromiseMan
313
439
  return cancelled;
314
440
  }
315
441
 
316
- /** 获取Promise状态 */
317
442
  public getStatus(id: number): FW.SystemDefine.FWPromiseStatus | null {
318
443
  const promiseProxy = this.promiseRegistry.get(id);
319
444
  return promiseProxy ? promiseProxy.status : null;
320
445
  }
321
446
 
322
- /** 获取所有Promise状态 */
323
447
  public getAllStatus(): Map<number, FW.SystemDefine.FWPromiseStatus> {
324
448
  const statusMap = new Map<number, FW.SystemDefine.FWPromiseStatus>();
325
449
  this.promiseRegistry.forEach((promiseProxy, id) => {
@@ -328,19 +452,16 @@ export default class FWPromiseManager extends FWManager implements FW.PromiseMan
328
452
  return statusMap;
329
453
  }
330
454
 
331
- /** 移除Promise */
332
455
  private removePromise(id: number): void {
333
456
  this.promiseRegistry.delete(id);
334
457
  }
335
458
 
336
- /** 获取正在执行的Promise数量 */
337
459
  public getActiveCount(): number {
338
460
  return Array.from(this.promiseRegistry.values()).filter(
339
461
  (p) => p.status === FW.SystemDefine.FWPromiseStatus.PENDING,
340
462
  ).length;
341
463
  }
342
464
 
343
- /** 清理已完成的Promise */
344
465
  public clearCompletedPromise(): number {
345
466
  const completedIds: number[] = [];
346
467
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ives_xxz/framework",
3
- "version": "2.1.23",
3
+ "version": "2.1.24",
4
4
  "description": "cocoscreator 2.x mvc framework",
5
5
  "main": "index.js",
6
6
  "keywords": ["123456"],
package/types/FW.d.ts CHANGED
@@ -2261,7 +2261,6 @@ declare namespace FW {
2261
2261
  id: number;
2262
2262
  status: any;
2263
2263
  promise?: Promise<T>;
2264
- abortController: AbortController;
2265
2264
  abort?: (reason?: any) => void;
2266
2265
  addAbortEventListener?: (
2267
2266
  listener: (this: AbortSignal, ev: Event) => any,