@idlebox/common 1.5.20 → 1.5.22
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/lib/{autoindex.d.ts → autoindex.generated.d.ts} +5 -4
- package/lib/autoindex.generated.d.ts.map +1 -0
- package/lib/{autoindex.js → autoindex.generated.js} +12 -6
- package/lib/autoindex.generated.js.map +1 -0
- package/lib/debugging/inspect.d.ts.map +1 -1
- package/lib/debugging/inspect.js +2 -1
- package/lib/debugging/inspect.js.map +1 -1
- package/lib/error/convert-unknown.d.ts.map +1 -1
- package/lib/error/convert-unknown.js +4 -0
- package/lib/error/convert-unknown.js.map +1 -1
- package/lib/error/pretty.nodejs.d.ts.map +1 -1
- package/lib/error/pretty.nodejs.js +4 -1
- package/lib/error/pretty.nodejs.js.map +1 -1
- package/lib/error/stack-parser.v8.js +2 -2
- package/lib/error/stack-parser.v8.js.map +1 -1
- package/lib/lifecycle/dispose/async-disposable.d.ts +11 -2
- package/lib/lifecycle/dispose/async-disposable.d.ts.map +1 -1
- package/lib/lifecycle/dispose/async-disposable.js +26 -6
- package/lib/lifecycle/dispose/async-disposable.js.map +1 -1
- package/lib/lifecycle/dispose/bridges/native.d.ts +2 -2
- package/lib/lifecycle/dispose/bridges/native.d.ts.map +1 -1
- package/lib/lifecycle/dispose/bridges/native.js +5 -5
- package/lib/lifecycle/dispose/bridges/native.js.map +1 -1
- package/lib/lifecycle/dispose/bridges/streams.d.ts +12 -0
- package/lib/lifecycle/dispose/bridges/streams.d.ts.map +1 -1
- package/lib/lifecycle/dispose/bridges/streams.js +30 -30
- package/lib/lifecycle/dispose/bridges/streams.js.map +1 -1
- package/lib/lifecycle/dispose/disposable.d.ts +11 -8
- package/lib/lifecycle/dispose/disposable.d.ts.map +1 -1
- package/lib/lifecycle/dispose/disposable.js +65 -27
- package/lib/lifecycle/dispose/disposable.js.map +1 -1
- package/lib/lifecycle/dispose/sync-disposable.d.ts +6 -1
- package/lib/lifecycle/dispose/sync-disposable.d.ts.map +1 -1
- package/lib/lifecycle/dispose/sync-disposable.js +21 -5
- package/lib/lifecycle/dispose/sync-disposable.js.map +1 -1
- package/lib/lifecycle/event/event.d.ts +27 -4
- package/lib/lifecycle/event/event.d.ts.map +1 -1
- package/lib/lifecycle/event/event.js +86 -37
- package/lib/lifecycle/event/event.js.map +1 -1
- package/lib/lifecycle/event/type.d.ts +2 -2
- package/lib/lifecycle/event/type.d.ts.map +1 -1
- package/lib/map-and-set/required-map.d.ts.map +1 -1
- package/lib/map-and-set/required-map.js +8 -0
- package/lib/map-and-set/required-map.js.map +1 -1
- package/lib/promise/deferred-promise.d.ts +1 -0
- package/lib/promise/deferred-promise.d.ts.map +1 -1
- package/lib/promise/deferred-promise.js +5 -5
- package/lib/promise/deferred-promise.js.map +1 -1
- package/lib/promise/is-promise.d.ts +2 -0
- package/lib/promise/is-promise.d.ts.map +1 -0
- package/lib/promise/is-promise.js +4 -0
- package/lib/promise/is-promise.js.map +1 -0
- package/lib/re-export.d.ts.map +1 -1
- package/lib/re-export.js +0 -1
- package/lib/re-export.js.map +1 -1
- package/lib/reflection/methods/bind.d.ts +1 -1
- package/lib/reflection/methods/bind.d.ts.map +1 -1
- package/lib/schedule/extendable-timer.d.ts +1 -1
- package/lib/schedule/extendable-timer.d.ts.map +1 -1
- package/lib/schedule/extendable-timer.js.map +1 -1
- package/lib/schedule/interval.d.ts +1 -1
- package/lib/schedule/interval.d.ts.map +1 -1
- package/lib/schedule/timeout.d.ts +3 -3
- package/lib/schedule/timeout.d.ts.map +1 -1
- package/lib/schedule/timeout.js +6 -9
- package/lib/schedule/timeout.js.map +1 -1
- package/lib/state/simple-state-machine.d.ts +1 -1
- package/lib/state/simple-state-machine.d.ts.map +1 -1
- package/package.json +12 -11
- package/src/{autoindex.ts → autoindex.generated.ts} +12 -9
- package/src/debugging/inspect.ts +2 -1
- package/src/error/convert-unknown.ts +4 -0
- package/src/error/pretty.nodejs.ts +5 -1
- package/src/error/stack-parser.v8.ts +2 -2
- package/src/lifecycle/dispose/async-disposable.ts +25 -6
- package/src/lifecycle/dispose/bridges/native.ts +5 -7
- package/src/lifecycle/dispose/bridges/streams.ts +46 -36
- package/src/lifecycle/dispose/disposable.ts +94 -35
- package/src/lifecycle/dispose/sync-disposable.ts +21 -5
- package/src/lifecycle/event/event.ts +84 -37
- package/src/lifecycle/event/type.ts +2 -2
- package/src/map-and-set/required-map.ts +10 -0
- package/src/promise/deferred-promise.ts +5 -6
- package/src/promise/is-promise.ts +3 -0
- package/src/re-export.ts +0 -1
- package/src/schedule/extendable-timer.ts +1 -2
- package/src/schedule/timeout.ts +10 -6
- package/lib/autoindex.d.ts.map +0 -1
- package/lib/autoindex.js.map +0 -1
- package/lib/schedule/local-type.d.ts +0 -3
- package/lib/schedule/local-type.d.ts.map +0 -1
- package/lib/schedule/local-type.js +0 -2
- package/lib/schedule/local-type.js.map +0 -1
- package/src/schedule/local-type.ts +0 -2
- package/src/string/concatType.generator.ts +0 -31
|
@@ -2,37 +2,42 @@ import { defineInspectMethod } from '../../../debugging/inspect.js';
|
|
|
2
2
|
import { objectName } from '../../../debugging/object-with-name.js';
|
|
3
3
|
import type { IAsyncDisposable } from '../disposable.js';
|
|
4
4
|
|
|
5
|
-
type ClosableAsync =
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
5
|
+
type ClosableAsync =
|
|
6
|
+
| {
|
|
7
|
+
closed?: boolean;
|
|
8
|
+
close(): Promise<any>;
|
|
9
|
+
}
|
|
10
|
+
| {
|
|
11
|
+
closed?: boolean;
|
|
12
|
+
close(cb: (e?: Error) => void): any;
|
|
13
|
+
};
|
|
9
14
|
|
|
10
15
|
/**
|
|
11
16
|
* Convert "close()"-able object to disposable
|
|
17
|
+
*
|
|
18
|
+
* FIXME: 关闭事件不同步
|
|
19
|
+
*
|
|
12
20
|
* @public
|
|
13
21
|
*/
|
|
14
22
|
export function closableToDisposable<T extends ClosableAsync>(closable: T): IAsyncDisposable {
|
|
15
|
-
const promised = closable.close.length === 0;
|
|
16
|
-
|
|
17
23
|
return defineInspectMethod(
|
|
18
24
|
{
|
|
19
25
|
get displayName() {
|
|
20
26
|
return `closable(${objectName(closable) || 'unknown'})`;
|
|
21
27
|
},
|
|
22
28
|
dispose(): Promise<void> {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
} else {
|
|
31
|
-
resolve();
|
|
32
|
-
}
|
|
33
|
-
});
|
|
29
|
+
return new Promise<void>((resolve, reject) => {
|
|
30
|
+
const mayPromise = closable.close((error) => {
|
|
31
|
+
if (error) {
|
|
32
|
+
reject(error);
|
|
33
|
+
} else {
|
|
34
|
+
resolve();
|
|
35
|
+
}
|
|
34
36
|
});
|
|
35
|
-
|
|
37
|
+
if (typeof mayPromise?.then === 'function') {
|
|
38
|
+
mayPromise.then(resolve, reject);
|
|
39
|
+
}
|
|
40
|
+
});
|
|
36
41
|
},
|
|
37
42
|
},
|
|
38
43
|
(_depth, options) => {
|
|
@@ -41,37 +46,42 @@ export function closableToDisposable<T extends ClosableAsync>(closable: T): IAsy
|
|
|
41
46
|
);
|
|
42
47
|
}
|
|
43
48
|
|
|
44
|
-
type EndableAsync =
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
49
|
+
type EndableAsync =
|
|
50
|
+
| {
|
|
51
|
+
ended?: boolean;
|
|
52
|
+
end(): Promise<any>;
|
|
53
|
+
}
|
|
54
|
+
| {
|
|
55
|
+
ended?: boolean;
|
|
56
|
+
end(cb: (e?: Error) => void): any;
|
|
57
|
+
};
|
|
48
58
|
|
|
49
59
|
/**
|
|
50
60
|
* Convert "end()"-able object to disposable
|
|
61
|
+
*
|
|
62
|
+
* FIXME: 关闭事件不同步
|
|
63
|
+
*
|
|
51
64
|
* @public
|
|
52
65
|
*/
|
|
53
66
|
export function endableToDisposable<T extends EndableAsync>(endable: T): IAsyncDisposable {
|
|
54
|
-
const promised = endable.end.length === 0;
|
|
55
|
-
|
|
56
67
|
return defineInspectMethod(
|
|
57
68
|
{
|
|
58
69
|
get displayName() {
|
|
59
70
|
return `endable(${objectName(endable) || 'unknown'})`;
|
|
60
71
|
},
|
|
61
72
|
dispose(): Promise<void> {
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
} else {
|
|
70
|
-
resolve();
|
|
71
|
-
}
|
|
72
|
-
});
|
|
73
|
+
return new Promise<void>((resolve, reject) => {
|
|
74
|
+
const mayPromise = endable.end((error) => {
|
|
75
|
+
if (error) {
|
|
76
|
+
reject(error);
|
|
77
|
+
} else {
|
|
78
|
+
resolve();
|
|
79
|
+
}
|
|
73
80
|
});
|
|
74
|
-
|
|
81
|
+
if (typeof mayPromise?.then === 'function') {
|
|
82
|
+
mayPromise.then(resolve, reject);
|
|
83
|
+
}
|
|
84
|
+
});
|
|
75
85
|
},
|
|
76
86
|
},
|
|
77
87
|
(_depth, options) => {
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { defineInspectMethod } from '../../debugging/inspect.js';
|
|
2
2
|
import type { MaybeNamed } from '../../debugging/object-with-name.js';
|
|
3
|
+
import { convertCaughtError } from '../../error/convert-unknown.js';
|
|
4
|
+
import { prettyPrintError } from '../../error/pretty.nodejs.js';
|
|
3
5
|
import { createStackTraceHolder, type StackTraceHolder } from '../../error/stack-trace.js';
|
|
4
|
-
import {
|
|
6
|
+
import { isPromiseLike } from '../../promise/is-promise.js';
|
|
5
7
|
import { Emitter } from '../event/event.js';
|
|
6
8
|
import type { EventRegister } from '../event/type.js';
|
|
7
9
|
import { fromNativeDisposable } from './bridges/native.js';
|
|
@@ -15,10 +17,14 @@ export enum DuplicateDisposeAction {
|
|
|
15
17
|
}
|
|
16
18
|
|
|
17
19
|
/** @public */
|
|
18
|
-
export interface IDisposableEvents {
|
|
19
|
-
onDisposeError: EventRegister<Error>;
|
|
20
|
-
|
|
21
|
-
readonly
|
|
20
|
+
export interface IDisposableEvents extends IBackReferenceDisposableEvent {
|
|
21
|
+
readonly onDisposeError: EventRegister<Error>;
|
|
22
|
+
readonly onPostDispose: EventRegister<void>;
|
|
23
|
+
readonly disposed: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export interface IBackReferenceDisposableEvent {
|
|
27
|
+
readonly onBeforeDispose: EventRegister<void>;
|
|
22
28
|
}
|
|
23
29
|
|
|
24
30
|
/** @public */
|
|
@@ -34,6 +40,22 @@ export interface IAsyncDisposable extends MaybeNamed {
|
|
|
34
40
|
type _Type<Async extends boolean> = Async extends true ? IAsyncDisposable : IDisposable;
|
|
35
41
|
type _RType<Async extends boolean> = Async extends true ? Promise<void> : void;
|
|
36
42
|
|
|
43
|
+
interface IDisposeState<Async extends boolean> {
|
|
44
|
+
/**
|
|
45
|
+
* 存在stack说明dispose已经开始(可能已经完成)
|
|
46
|
+
*/
|
|
47
|
+
trace?: StackTraceHolder;
|
|
48
|
+
finished: boolean;
|
|
49
|
+
/**
|
|
50
|
+
* 同步的是undefined,异步的是Promise
|
|
51
|
+
*/
|
|
52
|
+
result?: _RType<Async>;
|
|
53
|
+
/**
|
|
54
|
+
* 只有同步的用到,每次调用始终抛出相同错误,异步通过promise保存状态
|
|
55
|
+
*/
|
|
56
|
+
error?: Error;
|
|
57
|
+
}
|
|
58
|
+
|
|
37
59
|
/**
|
|
38
60
|
* 增强型Disposable
|
|
39
61
|
*/
|
|
@@ -69,11 +91,11 @@ export abstract class AbstractEnhancedDisposable<Async extends boolean> implemen
|
|
|
69
91
|
return `[Function debug]`;
|
|
70
92
|
});
|
|
71
93
|
|
|
72
|
-
this._onDisposeError =
|
|
94
|
+
this._onDisposeError = new Emitter<Error>(`${this.displayName}:errorEvent`, Emitter.EAction.PrintIgnore);
|
|
73
95
|
this.onDisposeError = this._onDisposeError.register;
|
|
74
|
-
this._onBeforeDispose =
|
|
96
|
+
this._onBeforeDispose = new Emitter<void>(`${this.displayName}:beforeEvent`, Emitter.EAction.PrintIgnore);
|
|
75
97
|
this.onBeforeDispose = this._onBeforeDispose.register;
|
|
76
|
-
this._onPostDispose = new Emitter<void>(`${this.displayName}:postEvent
|
|
98
|
+
this._onPostDispose = new Emitter<void>(`${this.displayName}:postEvent`, Emitter.EAction.PrintIgnore);
|
|
77
99
|
this.onPostDispose = this._onPostDispose.register;
|
|
78
100
|
}
|
|
79
101
|
|
|
@@ -81,8 +103,8 @@ export abstract class AbstractEnhancedDisposable<Async extends boolean> implemen
|
|
|
81
103
|
* @throws if already disposed
|
|
82
104
|
*/
|
|
83
105
|
public assertNotDisposed() {
|
|
84
|
-
if (this.
|
|
85
|
-
throw new DuplicateDisposedError(this, this.
|
|
106
|
+
if (this.__dispose_state.trace) {
|
|
107
|
+
throw new DuplicateDisposedError(this, this.__dispose_state.trace);
|
|
86
108
|
}
|
|
87
109
|
}
|
|
88
110
|
|
|
@@ -90,14 +112,15 @@ export abstract class AbstractEnhancedDisposable<Async extends boolean> implemen
|
|
|
90
112
|
* register a disposable object
|
|
91
113
|
*/
|
|
92
114
|
public _register<T extends _Type<Async>>(d: T): T;
|
|
93
|
-
public _register<T extends _Type<Async> &
|
|
115
|
+
public _register<T extends _Type<Async> & IBackReferenceDisposableEvent>(d: T, autoDereference?: boolean): T;
|
|
94
116
|
public _register(d: any, autoDereference?: boolean): any {
|
|
95
117
|
if (this._logger.enabled) this._logger(`register ${dispose_name(d)}`);
|
|
96
118
|
this.assertNotDisposed();
|
|
97
119
|
if (this._disposables.indexOf(d) !== -1) throw new Error(`disposable object ${dispose_name(d)} has already registed into "${dispose_name(this)}"`);
|
|
98
120
|
this._disposables.unshift(fromNativeDisposable(d));
|
|
99
121
|
if (autoDereference) {
|
|
100
|
-
(d as
|
|
122
|
+
(d as IBackReferenceDisposableEvent).onBeforeDispose(() => {
|
|
123
|
+
if (this.disposing || this.disposed) return;
|
|
101
124
|
this._unregister(d);
|
|
102
125
|
});
|
|
103
126
|
}
|
|
@@ -117,52 +140,88 @@ export abstract class AbstractEnhancedDisposable<Async extends boolean> implemen
|
|
|
117
140
|
return rmOk;
|
|
118
141
|
}
|
|
119
142
|
|
|
120
|
-
private
|
|
121
|
-
trace: StackTraceHolder;
|
|
122
|
-
result: _RType<Async>;
|
|
123
|
-
};
|
|
143
|
+
private __dispose_state: IDisposeState<Async> = { finished: false };
|
|
124
144
|
public get disposed() {
|
|
125
|
-
return
|
|
145
|
+
return this.__dispose_state.finished;
|
|
126
146
|
}
|
|
147
|
+
|
|
127
148
|
/**
|
|
128
|
-
*
|
|
149
|
+
* 正在dispose中(已开始但未完成)
|
|
129
150
|
*/
|
|
130
|
-
public get
|
|
131
|
-
return !!this.
|
|
151
|
+
public get disposing() {
|
|
152
|
+
return !this.__dispose_state.finished && !!this.__dispose_state.trace;
|
|
132
153
|
}
|
|
133
154
|
|
|
134
155
|
/**
|
|
135
156
|
* 释放相关资源
|
|
136
157
|
*/
|
|
137
158
|
public dispose(): _RType<Async> {
|
|
138
|
-
if (this.
|
|
139
|
-
|
|
159
|
+
if (this.__dispose_state.trace) {
|
|
160
|
+
// 释放已开始或已结束
|
|
161
|
+
if (this.duplicateDispose === DuplicateDisposeAction.Allow) {
|
|
162
|
+
if (this.__dispose_state.error) {
|
|
163
|
+
throw this.__dispose_state.error;
|
|
164
|
+
} else {
|
|
165
|
+
/**
|
|
166
|
+
* biome-ignore lint/style/noNonNullAssertion: 完全无需考虑
|
|
167
|
+
*
|
|
168
|
+
* 异步dispose的同步部分,重复调用dispose,会返回undefined而非Promise
|
|
169
|
+
* 但这正好是希望的,否则死锁了
|
|
170
|
+
*/
|
|
171
|
+
return this.__dispose_state.result!;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
140
174
|
|
|
141
|
-
const dupErr = new DuplicateDisposedError(this, this.
|
|
175
|
+
const dupErr = new DuplicateDisposedError(this, this.__dispose_state.trace);
|
|
142
176
|
dupErr.consoleWarning();
|
|
143
177
|
if (this.duplicateDispose === DuplicateDisposeAction.Disable) {
|
|
144
178
|
throw dupErr;
|
|
145
179
|
} else {
|
|
146
|
-
return this.
|
|
180
|
+
return this.__dispose_state.result as any;
|
|
147
181
|
}
|
|
182
|
+
// never
|
|
148
183
|
}
|
|
149
|
-
this._onBeforeDispose.fireNoError();
|
|
150
184
|
|
|
151
|
-
const r = this._dispose(this._disposables);
|
|
152
|
-
const trace = createStackTraceHolder('disposed', this.dispose);
|
|
153
185
|
const cleanup = () => {
|
|
154
|
-
|
|
155
|
-
// 记录 disposed 状态,顺便也记录调用栈
|
|
156
|
-
trace: trace,
|
|
157
|
-
result: r,
|
|
158
|
-
});
|
|
186
|
+
this.__dispose_state.finished = true;
|
|
159
187
|
|
|
160
188
|
Object.assign(this, { _disposables: null });
|
|
161
|
-
this._onPostDispose.
|
|
189
|
+
this._onPostDispose.fire();
|
|
162
190
|
this._onPostDispose.dispose();
|
|
191
|
+
|
|
192
|
+
this._onDisposeError.dispose();
|
|
163
193
|
};
|
|
164
|
-
|
|
165
|
-
|
|
194
|
+
|
|
195
|
+
// 第一时间设置trace
|
|
196
|
+
this.__dispose_state.trace = createStackTraceHolder('disposed', this.dispose);
|
|
197
|
+
|
|
198
|
+
this._onBeforeDispose.fire();
|
|
199
|
+
this._onBeforeDispose.dispose();
|
|
200
|
+
|
|
201
|
+
try {
|
|
202
|
+
this.__dispose_state.result = this._dispose(this._disposables);
|
|
203
|
+
} catch (e) {
|
|
204
|
+
// 同步错误处理
|
|
205
|
+
const err = convertCaughtError(e);
|
|
206
|
+
this.__dispose_state.error = err;
|
|
207
|
+
this._onDisposeError.fire(err);
|
|
208
|
+
if (this._onDisposeError.listenerCount() === 0) {
|
|
209
|
+
prettyPrintError('unhandled sync dispose error', err);
|
|
210
|
+
}
|
|
211
|
+
cleanup();
|
|
212
|
+
throw this.__dispose_state.error;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
const r = this.__dispose_state.result;
|
|
216
|
+
if (isPromiseLike(r)) {
|
|
217
|
+
// 异步错误处理
|
|
218
|
+
r.catch((e) => {
|
|
219
|
+
e = convertCaughtError(e);
|
|
220
|
+
this._onDisposeError.fire(e);
|
|
221
|
+
if (this._onDisposeError.listenerCount() === 0) {
|
|
222
|
+
prettyPrintError('unhandled async dispose error', e);
|
|
223
|
+
}
|
|
224
|
+
}).finally(cleanup);
|
|
166
225
|
} else {
|
|
167
226
|
cleanup();
|
|
168
227
|
}
|
|
@@ -10,7 +10,7 @@ import { DuplicateDisposedError } from './disposedError.js';
|
|
|
10
10
|
export abstract class DisposableOnce implements IDisposable {
|
|
11
11
|
private _disposed?: StackTraceHolder;
|
|
12
12
|
|
|
13
|
-
public get
|
|
13
|
+
public get disposed() {
|
|
14
14
|
return !!this._disposed;
|
|
15
15
|
}
|
|
16
16
|
public dispose(): void {
|
|
@@ -32,21 +32,37 @@ export abstract class DisposableOnce implements IDisposable {
|
|
|
32
32
|
* 完整版disposable类
|
|
33
33
|
* 可以继承
|
|
34
34
|
* 也可以直接用(相当于一个DisposableStack)
|
|
35
|
+
*
|
|
36
|
+
* 抛出异常的行为会延迟到所有资源都尝试释放完毕之后,因此显然只会抛出其中一个异常给dispose()的调用者
|
|
37
|
+
* 每一个资源释放失败,都会分别触发onDisposeError事件,如果此事件存在监听器,则最后不会抛出异常(处非监听器本身重新抛出)
|
|
38
|
+
* 不支持在dispose过程中添加onError事件监听器
|
|
39
|
+
*
|
|
35
40
|
*/
|
|
36
41
|
export class EnhancedDisposable extends AbstractEnhancedDisposable<false> implements IDisposable {
|
|
37
42
|
protected override _dispose(disposables: readonly IDisposable[]): void {
|
|
43
|
+
const hasListener = this._onDisposeError.listenerCount() > 0;
|
|
44
|
+
|
|
45
|
+
let lastError = null;
|
|
38
46
|
for (const item of disposables.values()) {
|
|
39
47
|
try {
|
|
40
48
|
if (this._logger.enabled) this._logger(`dispose ${dispose_name(item)}`);
|
|
41
49
|
item.dispose();
|
|
42
50
|
} catch (e) {
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
51
|
+
if (hasListener) {
|
|
52
|
+
try {
|
|
53
|
+
this._onDisposeError.fire(convertCaughtError(e));
|
|
54
|
+
} catch (e) {
|
|
55
|
+
lastError = e;
|
|
56
|
+
}
|
|
57
|
+
} else {
|
|
58
|
+
lastError = e;
|
|
47
59
|
}
|
|
48
60
|
}
|
|
49
61
|
}
|
|
62
|
+
|
|
63
|
+
if (lastError) {
|
|
64
|
+
throw lastError;
|
|
65
|
+
}
|
|
50
66
|
}
|
|
51
67
|
|
|
52
68
|
/// compitable with stack
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
-
import { Exit } from '@idlebox/errors';
|
|
2
1
|
import { defineInspectMethod, inspectSymbol } from '../../debugging/inspect.js';
|
|
3
2
|
import { nameObject, objectName } from '../../debugging/object-with-name.js';
|
|
3
|
+
import { convertCaughtError } from '../../error/convert-unknown.js';
|
|
4
|
+
import { prettyPrintError } from '../../error/pretty.nodejs.js';
|
|
4
5
|
import { createStackTraceHolder } from '../../error/stack-trace.js';
|
|
5
6
|
import { functionToDisposable } from '../dispose/bridges/function.js';
|
|
6
7
|
import type { IDisposable } from '../dispose/disposable.js';
|
|
@@ -9,15 +10,27 @@ import type { EventHandler, EventRegister, IEventEmitter } from './type.js';
|
|
|
9
10
|
|
|
10
11
|
const anonymousName = 'AnonymousEmitter';
|
|
11
12
|
|
|
13
|
+
enum FireErrorAction {
|
|
14
|
+
Throw = 0,
|
|
15
|
+
Delay = 1,
|
|
16
|
+
Ignore = 2,
|
|
17
|
+
PrintIgnore = 3,
|
|
18
|
+
}
|
|
19
|
+
|
|
12
20
|
/**
|
|
13
21
|
* @public
|
|
14
22
|
*/
|
|
15
23
|
export class Emitter<T = unknown> implements IEventEmitter<T> {
|
|
16
|
-
protected _callbacks
|
|
24
|
+
protected readonly _callbacks: (EventHandler<T> | undefined)[] = [];
|
|
17
25
|
private executing = false;
|
|
18
26
|
private _something_change_during_call = false;
|
|
19
27
|
|
|
20
|
-
|
|
28
|
+
static readonly EAction = FireErrorAction;
|
|
29
|
+
|
|
30
|
+
constructor(
|
|
31
|
+
public readonly displayName: string = anonymousName,
|
|
32
|
+
private readonly onErrorDefault: FireErrorAction = FireErrorAction.Throw,
|
|
33
|
+
) {
|
|
21
34
|
this.handle = Object.defineProperties(this.handle.bind(this), {
|
|
22
35
|
once: {
|
|
23
36
|
get: () => this.once.bind(this),
|
|
@@ -25,7 +38,7 @@ export class Emitter<T = unknown> implements IEventEmitter<T> {
|
|
|
25
38
|
wait: {
|
|
26
39
|
get: () => this.wait.bind(this),
|
|
27
40
|
},
|
|
28
|
-
|
|
41
|
+
disposed: {
|
|
29
42
|
get: () => this._disposed,
|
|
30
43
|
},
|
|
31
44
|
});
|
|
@@ -35,17 +48,69 @@ export class Emitter<T = unknown> implements IEventEmitter<T> {
|
|
|
35
48
|
}
|
|
36
49
|
|
|
37
50
|
public listenerCount() {
|
|
38
|
-
return this._callbacks
|
|
51
|
+
return this._callbacks.length;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
private __fireThrow(data: T) {
|
|
55
|
+
for (const callback of this._callbacks) {
|
|
56
|
+
callback?.(data);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
private __fireDelay(data: T) {
|
|
60
|
+
const errors: Error[] = [];
|
|
61
|
+
for (const callback of this._callbacks) {
|
|
62
|
+
try {
|
|
63
|
+
callback?.(data);
|
|
64
|
+
} catch (e) {
|
|
65
|
+
errors.push(convertCaughtError(e));
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
return errors;
|
|
69
|
+
}
|
|
70
|
+
private __fireIgnore(data: T) {
|
|
71
|
+
for (const callback of this._callbacks) {
|
|
72
|
+
try {
|
|
73
|
+
callback?.(data);
|
|
74
|
+
} catch {}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
private __firePrintIgnore(data: T) {
|
|
78
|
+
for (const callback of this._callbacks) {
|
|
79
|
+
try {
|
|
80
|
+
callback?.(data);
|
|
81
|
+
} catch (e) {
|
|
82
|
+
const ee = convertCaughtError(e);
|
|
83
|
+
prettyPrintError('error while handling event', ee);
|
|
84
|
+
}
|
|
85
|
+
}
|
|
39
86
|
}
|
|
40
87
|
|
|
41
|
-
|
|
88
|
+
/**
|
|
89
|
+
* @param data
|
|
90
|
+
* @param error {Emitter.EAction} 如何处理错误
|
|
91
|
+
* - Throw: 默认行为,遇到错误立即抛出,后续监听器不再被调用
|
|
92
|
+
* - Delay: 等所有监听器都调用完后,如果有错误则抛出AggregateError,包含所有错误
|
|
93
|
+
* - Ignore: 忽略所有错误,继续调用全部监听器
|
|
94
|
+
* - PrintIgnore: 忽略所有错误,但打印错误信息
|
|
95
|
+
* @returns
|
|
96
|
+
*/
|
|
97
|
+
public fire(data: T, error = this.onErrorDefault) {
|
|
42
98
|
this.requireNotExecuting();
|
|
43
|
-
if (!this._callbacks) return;
|
|
99
|
+
if (!this._callbacks.length) return;
|
|
44
100
|
|
|
45
101
|
this.executing = true;
|
|
46
102
|
try {
|
|
47
|
-
|
|
48
|
-
|
|
103
|
+
if (error === FireErrorAction.Throw) {
|
|
104
|
+
this.__fireThrow(data);
|
|
105
|
+
} else if (error === FireErrorAction.Delay) {
|
|
106
|
+
const errors = this.__fireDelay(data);
|
|
107
|
+
if (errors.length) {
|
|
108
|
+
throw new AggregateError(errors, 'multiple errors while handling event');
|
|
109
|
+
}
|
|
110
|
+
} else if (error === FireErrorAction.Ignore) {
|
|
111
|
+
this.__fireIgnore(data);
|
|
112
|
+
} else if (error === FireErrorAction.PrintIgnore) {
|
|
113
|
+
this.__firePrintIgnore(data);
|
|
49
114
|
}
|
|
50
115
|
} finally {
|
|
51
116
|
this.executing = false;
|
|
@@ -55,25 +120,9 @@ export class Emitter<T = unknown> implements IEventEmitter<T> {
|
|
|
55
120
|
}
|
|
56
121
|
}
|
|
57
122
|
|
|
123
|
+
/** @deprecated use fire(data, Emitter.Error.Ignore) */
|
|
58
124
|
public fireNoError(data: T) {
|
|
59
|
-
this.
|
|
60
|
-
if (!this._callbacks) return;
|
|
61
|
-
|
|
62
|
-
this.executing = true;
|
|
63
|
-
for (const callback of this._callbacks) {
|
|
64
|
-
try {
|
|
65
|
-
callback?.(data);
|
|
66
|
-
} catch (e) {
|
|
67
|
-
if (e instanceof Exit) {
|
|
68
|
-
break;
|
|
69
|
-
}
|
|
70
|
-
console.error('Emitter.fireNoError: error ignored: %s', e instanceof Error ? e.stack : e);
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
this.executing = false;
|
|
74
|
-
if (this._something_change_during_call) {
|
|
75
|
-
this.checkDeleted();
|
|
76
|
-
}
|
|
125
|
+
this.fire(data, FireErrorAction.Ignore);
|
|
77
126
|
}
|
|
78
127
|
|
|
79
128
|
get register(): EventRegister<T> {
|
|
@@ -91,20 +140,17 @@ export class Emitter<T = unknown> implements IEventEmitter<T> {
|
|
|
91
140
|
handle(callback: EventHandler<T>): IDisposable {
|
|
92
141
|
this.requireNotExecuting();
|
|
93
142
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
const callbacks = this._callbacks;
|
|
97
|
-
callbacks.unshift(callback);
|
|
143
|
+
this._callbacks.unshift(callback);
|
|
98
144
|
|
|
99
145
|
return functionToDisposable(
|
|
100
146
|
nameObject(`removeListener(${objectName(callback)})`, () => {
|
|
101
|
-
const index =
|
|
147
|
+
const index = this._callbacks.indexOf(callback);
|
|
102
148
|
if (index === -1) return;
|
|
103
149
|
if (this.executing) {
|
|
104
150
|
this._something_change_during_call = true;
|
|
105
|
-
|
|
151
|
+
this._callbacks[index] = undefined;
|
|
106
152
|
} else {
|
|
107
|
-
|
|
153
|
+
this._callbacks.splice(index, 1);
|
|
108
154
|
}
|
|
109
155
|
}),
|
|
110
156
|
);
|
|
@@ -147,7 +193,7 @@ export class Emitter<T = unknown> implements IEventEmitter<T> {
|
|
|
147
193
|
}
|
|
148
194
|
|
|
149
195
|
private _disposed = false;
|
|
150
|
-
public get
|
|
196
|
+
public get disposed() {
|
|
151
197
|
return this._disposed;
|
|
152
198
|
}
|
|
153
199
|
|
|
@@ -159,7 +205,8 @@ export class Emitter<T = unknown> implements IEventEmitter<T> {
|
|
|
159
205
|
if (this._disposed) return;
|
|
160
206
|
this._disposed = true;
|
|
161
207
|
|
|
162
|
-
this._callbacks =
|
|
208
|
+
this._callbacks.length = 0;
|
|
209
|
+
Object.assign(this, { _callbacks: null });
|
|
163
210
|
|
|
164
211
|
if (this._waittings) {
|
|
165
212
|
for (const rej of this._waittings) {
|
|
@@ -169,7 +216,7 @@ export class Emitter<T = unknown> implements IEventEmitter<T> {
|
|
|
169
216
|
|
|
170
217
|
this._waittings = undefined;
|
|
171
218
|
|
|
172
|
-
const trace = createStackTraceHolder('disposed');
|
|
219
|
+
const trace = createStackTraceHolder('disposed', this.dispose);
|
|
173
220
|
|
|
174
221
|
const makeUnCallable = (name: string) => {
|
|
175
222
|
Object.assign(this, {
|
|
@@ -53,7 +53,7 @@ export interface IEventEmitter<T = unknown> extends IDisposable {
|
|
|
53
53
|
*/
|
|
54
54
|
wait(): Promise<T>;
|
|
55
55
|
|
|
56
|
-
readonly
|
|
56
|
+
readonly disposed: boolean;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
59
|
export type EventHandler<T> = (data: T) => void;
|
|
@@ -65,7 +65,7 @@ export interface EventRegister<T> {
|
|
|
65
65
|
(callback: EventHandler<T>): IDisposable;
|
|
66
66
|
once(callback: EventHandler<T>): IDisposable;
|
|
67
67
|
wait(): IDisposable;
|
|
68
|
-
readonly
|
|
68
|
+
readonly disposed: boolean;
|
|
69
69
|
}
|
|
70
70
|
|
|
71
71
|
export type EventEmitterMap<T extends Record<string, unknown>> = {
|
|
@@ -37,6 +37,10 @@ export class RequiredMap<K, V> extends Map<K, V> {
|
|
|
37
37
|
}
|
|
38
38
|
}
|
|
39
39
|
|
|
40
|
+
if (Map.prototype.getOrInsert) {
|
|
41
|
+
RequiredMap.prototype.get = Map.prototype.getOrInsert as any;
|
|
42
|
+
}
|
|
43
|
+
|
|
40
44
|
/**
|
|
41
45
|
* A map that holds instances, automatically create new instance
|
|
42
46
|
*/
|
|
@@ -56,3 +60,9 @@ export abstract class InstanceMap<K, V> extends Map<K, V> {
|
|
|
56
60
|
return nv;
|
|
57
61
|
}
|
|
58
62
|
}
|
|
63
|
+
|
|
64
|
+
if (typeof Map.prototype.getOrInsertComputed === 'function') {
|
|
65
|
+
RequiredMap.prototype.get = function (this: InstanceMap<any, any>, id: any): any {
|
|
66
|
+
return this.getOrInsertComputed(id, (k) => this.instance(k));
|
|
67
|
+
};
|
|
68
|
+
}
|
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
import { CanceledError, TimeoutError } from '@idlebox/errors';
|
|
2
2
|
import type { IDisposable } from '../lifecycle/dispose/disposable.js';
|
|
3
|
-
import type { TimeoutType } from '../schedule/local-type.js';
|
|
4
3
|
import { scheduler } from '../schedule/scheduler.js';
|
|
5
4
|
|
|
6
5
|
export type ValueCallback<T = any> = (value: T | Promise<T>) => void;
|
|
@@ -143,16 +142,16 @@ export class DeferredPromise<T, PT = any> {
|
|
|
143
142
|
};
|
|
144
143
|
}
|
|
145
144
|
|
|
146
|
-
|
|
145
|
+
private timer?: ITimeoutType;
|
|
147
146
|
#cancel_timeout() {
|
|
148
|
-
if (this
|
|
149
|
-
clearTimeout(this
|
|
150
|
-
this
|
|
147
|
+
if (this.timer) {
|
|
148
|
+
clearTimeout(this.timer);
|
|
149
|
+
this.timer = undefined;
|
|
151
150
|
}
|
|
152
151
|
}
|
|
153
152
|
timeout(ms: number) {
|
|
154
153
|
if (this.settled) throw new Error('no more timeout after settled');
|
|
155
|
-
this
|
|
154
|
+
this.timer = setTimeout(() => {
|
|
156
155
|
this.error(new TimeoutError(ms, 'promise not settled'));
|
|
157
156
|
}, ms);
|
|
158
157
|
|
package/src/re-export.ts
CHANGED
|
@@ -1,12 +1,11 @@
|
|
|
1
1
|
import { DeferredPromise } from '../promise/deferred-promise.js';
|
|
2
|
-
import type { TimeoutType } from './local-type.js';
|
|
3
2
|
|
|
4
3
|
/**
|
|
5
4
|
* 反复推迟的 setTimeout
|
|
6
5
|
*/
|
|
7
6
|
export class ExtendableTimer {
|
|
8
7
|
private readonly dfd = new DeferredPromise<void>();
|
|
9
|
-
|
|
8
|
+
public tmr?: ITimeoutType;
|
|
10
9
|
|
|
11
10
|
constructor(private readonly durationMs: number) {}
|
|
12
11
|
|