-
-
Notifications
You must be signed in to change notification settings - Fork 0
Retainer
Eugene Lazutkin edited this page May 7, 2026
·
7 revisions
Since version 1.1.0.
Retainer is a utility class designed to manage the lifecycle of expensive resources. It creates resources on demand, keeps them alive while they're being used, and automatically destroys them after a specified retention period when they're no longer needed. This pattern is particularly useful for optimizing resource usage in applications that need to balance performance with memory efficiency.
- Resource Lifecycle Management: Automatically handles creation and destruction of resources.
- Reference Counting: Tracks the number of active references to a resource.
- Delayed Cleanup: Retains resources for a configurable period after the last reference is released.
- Async Support: Works with asynchronous resource creation and destruction operations.
Retainer is available as a named and default export.
const retainer = new Retainer({
create: async () => {
/* create and return a resource */
},
destroy: async resource => {
/* clean up the resource */
},
retentionPeriod: 1000 // milliseconds
});-
options: An object containing the following properties:-
create: A function that returns a Promise resolving to the created resource. -
destroy: A function that takes the resource and returns a Promise that resolves when the resource is destroyed. -
retentionPeriod: The time in milliseconds to retain the resource after the last reference is released. Default is 1000ms (1 second).
-
-
create: The function used to create the resource. -
destroy: The function used to destroy the resource. -
retentionPeriod: The time in milliseconds to retain the resource after the last reference is released. -
counter: The number of active references to the resource. -
value(read-only): The retained resource, ornullif no resource is currently retained. Managed internally — do not assign.
-
async get(): Retrieves the retained resource, creating it if necessary. Increments the reference counter. Returns a promise that resolves to the resource. Concurrent-safe: multipleget()calls made before the firstcreate()resolves all share the same in-flight promise —create()runs at most once per zero-counter interval. -
async release(immediately): Decrements the reference counter and schedules the resource for destruction if the counter reaches zero. Ifimmediatelyis true, the resource is destroyed immediately instead of after the retention period. Returns a promise that resolves to the retainer instance. Errors thrown bydestroy()propagate fromrelease(true)(you canawaitandcatch); in the deferred path they surface as standard unhandled rejection events.
To use the Retainer class, import it into your project and create an instance with appropriate creation and destruction functions:
import Retainer from 'time-queues/Retainer.js';
// Example: Managing a database connection
const dbConnectionRetainer = new Retainer({
create: async () => {
console.log('Creating database connection');
return await openDatabaseConnection();
},
destroy: async connection => {
console.log('Closing database connection');
await connection.close();
},
retentionPeriod: 5000 // Keep the connection for 5 seconds after last use
});
// Using the resource
async function performDatabaseOperation() {
const connection = await dbConnectionRetainer.get();
try {
// Use the connection...
await connection.query('SELECT * FROM users');
} finally {
// Release the connection when done
await dbConnectionRetainer.release();
}
}
// Multiple functions can use the same resource
async function performMultipleOperations() {
// These operations will share the same connection
await performDatabaseOperation();
await performDatabaseOperation();
// The connection will be closed 5 seconds after the last operation completes
}-
Resource Not Being Created: Ensure that the
createfunction is correctly implemented and returns a Promise that resolves to the resource. -
Resource Not Being Destroyed: Check that the
destroyfunction is correctly implemented and properly cleans up the resource. -
Memory Leaks: If you're experiencing memory leaks, ensure that all calls to
get()are paired with calls torelease(). Consider usingtry/finallyblocks to ensure resources are released even if errors occur. -
Resource Being Destroyed Too Early: Increase the
retentionPeriodif the resource is being destroyed before you're done with it. -
Resource Not Being Destroyed: If the resource isn't being destroyed when expected, check if all references have been released by ensuring that the number of
release()calls matches the number ofget()calls.
- Counter - Task counting utility
- LimitedQueue - Queue with concurrency control