@freemework/common
Version:
Common library of the Freemework Project.
138 lines • 5.23 kB
JavaScript
import { FException, FExceptionAggregate, FExceptionInvalidOperation } from "../exception/index.js";
import "./tc39.js";
export class FDisposable {
async [Symbol.asyncDispose]() {
await this.dispose();
}
static async disposeAll(...instances) {
const innerExceptions = [];
for (const instance of instances) {
try {
await instance.dispose();
}
catch (e) {
innerExceptions.push(FException.wrapIfNeeded(e));
}
}
FExceptionAggregate.throwIfNeeded(innerExceptions);
}
static instanceOf(test) {
if (test instanceof FDisposable) {
return true;
}
if (typeof test === "object"
&& test !== null // {}
&& "dispose" in test // { init: ... }
&& typeof test.dispose === "function" // { dispose: function(...) {} }
&& test.dispose.length === 0 // { dispose: function() {} }
) {
return true;
}
return false;
}
}
export class FDisposableBase extends FDisposable {
_disposed;
_disposingPromise;
get disposed() { return this._disposed === true; }
get disposing() { return this._disposingPromise !== undefined; }
async dispose() {
if (this._disposed !== true) {
if (this._disposingPromise === undefined) {
this._disposingPromise = Promise.resolve();
const onDisposeResult = this.onDispose();
if (onDisposeResult instanceof Promise) {
this._disposingPromise = this._disposingPromise
.then(() => onDisposeResult)
.finally(() => {
delete this._disposingPromise;
this._disposed = true;
});
return this._disposingPromise;
}
else {
this._disposed = true;
delete this._disposingPromise;
}
}
else {
return this._disposingPromise;
}
}
return Promise.resolve();
}
verifyNotDisposed() {
if (this.disposed || this.disposing) {
throw new Error("Wrong operation on disposed object");
}
}
}
export class FDisposableMixin extends FDisposableBase {
static applyMixin(targetClass) {
let sourceType = FDisposableBase;
while (sourceType.prototype !== undefined) {
Object.getOwnPropertySymbols(sourceType.prototype).forEach(name => {
const propertyDescriptor = Object.getOwnPropertyDescriptor(sourceType.prototype, name);
if (propertyDescriptor !== undefined) {
Object.defineProperty(targetClass.prototype, name, propertyDescriptor);
}
});
Object.getOwnPropertyNames(sourceType.prototype).forEach(name => {
if (name === "constructor") {
// Skip constructor
return;
}
const propertyDescriptor = Object.getOwnPropertyDescriptor(sourceType.prototype, name);
if (propertyDescriptor !== undefined) {
Object.defineProperty(targetClass.prototype, name, propertyDescriptor);
}
});
sourceType = Object.getPrototypeOf(sourceType);
}
Object.getOwnPropertyNames(FDisposableMixin.prototype).forEach(name => {
if (name === "constructor") {
// Skip constructor
return;
}
const propertyDescriptor = Object.getOwnPropertyDescriptor(FDisposableMixin.prototype, name);
if (name === "onDispose") {
// Add NOP methods into mixed only if it not implements its
if (propertyDescriptor !== undefined) {
const existingPropertyDescriptor = Object.getOwnPropertyDescriptor(targetClass.prototype, name);
if (existingPropertyDescriptor === undefined) {
Object.defineProperty(targetClass.prototype, name, propertyDescriptor);
}
}
return;
}
if (propertyDescriptor !== undefined) {
Object.defineProperty(targetClass.prototype, name, propertyDescriptor);
}
});
}
onDispose() {
// Do nothing here by design. Users will override this method.
}
constructor() {
super();
// Never called, due mixin
// Private constructor has two kinds of responsibility
// 1) Restrict to extends the mixin
// 2) Restrict to make instances of the mixin
throw new FExceptionInvalidOperation("Private constructor");
}
}
class FDisposableAdapter extends FDisposable {
_dispose;
constructor(_dispose) {
super();
this._dispose = _dispose;
}
async dispose() {
await this._dispose();
}
}
export function makeDisposable(dispose) {
return new FDisposableAdapter(dispose);
}
//# sourceMappingURL=f_disposable.js.map