@esfx/disposable
Version:
A low-level API for defining explicit resource management.
158 lines (155 loc) • 5.93 kB
JavaScript
;
/*!
Copyright 2019 Ron Buckton
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.AsyncDisposable = void 0;
const utils_js_1 = require("./internal/utils.js");
/**
* Indicates an object that has resources that can be explicitly disposed asynchronously.
*
* NOTE: It is not necessary to subclass `AsyncDisposable`. Merely having an `[AsyncDisposable.asyncDispose]()` method is sufficient.
*/
class AsyncDisposable {
/**
* Creates an `AsyncDisposable` wrapper around a callback used to dispose resources.
* @deprecated Use `AsyncDisposableStack` or `{ [AsyncDisposable.asyncDispose]() { ... } }` instead.
*/
constructor(disposeAsync) {
if (!(typeof disposeAsync === "function"))
throw new TypeError("Function expected: disposeAsync");
return AsyncDisposable.create(disposeAsync);
}
}
exports.AsyncDisposable = AsyncDisposable;
(function (AsyncDisposable) {
/**
* A well-known symbol used to define an async explicit resource disposal method on an object.
*
* NOTE: Uses `Symbol.asyncDispose` if present.
*/
AsyncDisposable.asyncDispose = typeof Symbol["asyncDispose"] === "symbol" ?
Symbol["asyncDispose"] :
Symbol.for("@esfx/disposable:AsyncDisposable.asyncDispose");
/**
* Emulate `using await const` using `for..await..of`.
*
* NOTE: This is not spec-compliant and will not be standardized.
*
* @example
* ```ts
* // with `using await const` (proposed)
* {
* ...
* using await const x = expr, y = expr;
* ...
* }
*
* // with `AsyncDisposable.scope()`:
* for await (const { using, fail } of AsyncDisposable.scope()) {
* try {
* ...
* const x = using(expr), y = using(expr);
* ...
* }
* catch (e) {
* fail(e);
* }
* }
* ```
*/
async function* scope() {
const context = (0, utils_js_1.CreateScope)("async");
try {
context.state = "initialized";
yield context.scope;
context.state = "exiting";
}
finally {
context.state = "done";
await (0, utils_js_1.DisposeResources)("async", context.disposables, context.throwCompletion);
}
}
AsyncDisposable.scope = scope;
/**
* Yields each disposable in the iterable, disposing it when the generator resumes.
*
* This emulates `for (using await const x of expr)`.
*
* NOTE: This is not spec-compliant and will not be standardized.
*
* @example
* ```ts
* // with `using await const` (proposed)
* for (using await const x of expr) {
* ...
* }
* for await (using await const x of expr) {
* ...
* }
*
* // with `Disposable.usingEach()`:
* for await (const x of Disposable.usingEach(expr)) {
* ...
* }
* ```
*/
async function* usingEach(iterable) {
if (!((typeof iterable === "object" && iterable !== null || typeof iterable === "function") && Symbol.asyncIterator in iterable) && !((typeof iterable === "object" && iterable !== null || typeof iterable === "function") && Symbol.iterator in iterable))
throw new TypeError("Object not iterable: iterable");
for await (const disposable of iterable) {
for await (const { using, fail } of AsyncDisposable.scope())
try {
yield using(disposable);
}
catch (e) {
fail(e);
}
}
}
AsyncDisposable.usingEach = usingEach;
const asyncDisposablePrototype = AsyncDisposable.prototype;
Object.defineProperty(asyncDisposablePrototype, Symbol.toStringTag, { configurable: true, value: "AsyncDisposable" });
/**
* Creates an `AsyncDisposable` wrapper around a callback used to dispose resources.
*
* NOTE: This is not spec-compliant and will not be standardized. It is preferred to use an `AsyncDisposableStack`
* or to implement `AsyncDisposable.asyncDispose` yourself instead.
*/
function create(disposeAsync) {
if (!(typeof disposeAsync === "function"))
throw new TypeError("Function expected: disposeAsync");
let disposed = false;
return Object.setPrototypeOf({
async [AsyncDisposable.asyncDispose]() {
if (!disposed) {
disposed = true;
const cb = disposeAsync;
disposeAsync = undefined;
await cb();
}
}
}, asyncDisposablePrototype);
}
AsyncDisposable.create = create;
/**
* Determines whether a value is `AsyncDisposable`.
*
* NOTE: This is not spec-compliant and will not be standardized.
*/
function hasInstance(value) {
return (typeof value === "object" && value !== null || typeof value === "function") && AsyncDisposable.asyncDispose in value;
}
AsyncDisposable.hasInstance = hasInstance;
})(AsyncDisposable = exports.AsyncDisposable || (exports.AsyncDisposable = {}));
Object.defineProperty(AsyncDisposable, Symbol.hasInstance, Object.getOwnPropertyDescriptor(AsyncDisposable, "hasInstance"));
//# sourceMappingURL=asyncDisposable.js.map