Skip to content

Retainer

Eugene Lazutkin edited this page May 7, 2026 · 7 revisions

Description

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.

Key features

  • 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.

Technical specifications

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
});

Constructor parameters

  • 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).

Instance properties

  • 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, or null if no resource is currently retained. Managed internally — do not assign.

Instance methods

  • async get(): Retrieves the retained resource, creating it if necessary. Increments the reference counter. Returns a promise that resolves to the resource. Concurrent-safe: multiple get() calls made before the first create() 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. If immediately is true, the resource is destroyed immediately instead of after the retention period. Returns a promise that resolves to the retainer instance. Errors thrown by destroy() propagate from release(true) (you can await and catch); in the deferred path they surface as standard unhandled rejection events.

Usage instructions

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
}

Troubleshooting

  • Resource Not Being Created: Ensure that the create function is correctly implemented and returns a Promise that resolves to the resource.
  • Resource Not Being Destroyed: Check that the destroy function 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 to release(). Consider using try/finally blocks to ensure resources are released even if errors occur.
  • Resource Being Destroyed Too Early: Increase the retentionPeriod if 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 of get() calls.

See Also

Clone this wiki locally