When working with async operations, carefully manage execution context and resource references across await boundaries to prevent context corruption and resource leaks.

For execution context, avoid patterns where context enters/exits span async boundaries, as context may not be properly restored after await points. Instead, structure code to contain async operations within the context scope:

// Problematic - context lost across await
if (TRACING_ENABLED) {
  span = builtinTracer().startSpan(this.method, { kind: 2 });
  context = enterSpan(span);
  // await operations here lose context
}

// Better - use IIFE to contain async operations
const old = getAsyncContext();
try {
  setAsyncContext('inside operation');
  return (async () => {
    await asyncOperation();
    // context preserved here
  })();
} finally {
  setAsyncContext(old);
}

For resource management, distinguish between ongoing and future async operations when managing references. Use unref/ref patterns to handle current operations without affecting future ones:

// Unref ongoing reads but not future reads
if (this.unref) {
  this.unref();  // Clear current operation references
  this.ref();    // Re-establish for future operations
}

This prevents resource leaks while maintaining proper reference counting for subsequent async operations.