UNPKG

@freemework/common

Version:

Common library of the Freemework Project.

138 lines 5.23 kB
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