Deprecated utilities package for Sentry JavaScript SDKs - all functionality moved to @sentry/core
—
DEPRECATED: Import all functions from @sentry/core instead of @sentry/utils.
Synchronous promise implementation and promise buffer management for reliable async operations and controlled concurrency.
Synchronous promise implementation that executes immediately without microtask scheduling.
/**
* Synchronous promise implementation that executes immediately
* @template T - Type of the resolved value
*/
class SyncPromise<T> implements PromiseLike<T> {
/**
* Creates a new SyncPromise
* @param executor - Function that initializes the promise
*/
constructor(
executor: (
resolve: (value: T | PromiseLike<T>) => void,
reject: (reason?: any) => void
) => void
);
/**
* Attaches callbacks for resolution and/or rejection
* @param onfulfilled - Callback for successful resolution
* @param onrejected - Callback for rejection
* @returns New SyncPromise with transformed value
*/
then<TResult1 = T, TResult2 = never>(
onfulfilled?: ((value: T) => TResult1 | PromiseLike<TResult1>) | null,
onrejected?: ((reason: any) => TResult2 | PromiseLike<TResult2>) | null,
): SyncPromise<TResult1 | TResult2>;
/**
* Attaches a callback for rejection
* @param onrejected - Callback for rejection
* @returns New SyncPromise
*/
catch<TResult = never>(
onrejected?: ((reason: any) => TResult | PromiseLike<TResult>) | null,
): SyncPromise<T | TResult>;
/**
* Returns a resolved SyncPromise with the given value
* @param value - Value to resolve with
* @returns Resolved SyncPromise
*/
static resolve<T>(value: T | PromiseLike<T>): SyncPromise<T>;
/**
* Returns a rejected SyncPromise with the given reason
* @param reason - Reason for rejection
* @returns Rejected SyncPromise
*/
static reject<T = never>(reason?: any): SyncPromise<T>;
}Convenient factory functions for creating resolved and rejected SyncPromises.
/**
* Creates a resolved SyncPromise with the given value
* @param value - Value to resolve with
* @returns Resolved SyncPromise
*/
function resolvedSyncPromise<T>(value: T): SyncPromise<T>;
/**
* Creates a rejected SyncPromise with the given reason
* @param reason - Reason for rejection
* @returns Rejected SyncPromise
*/
function rejectedSyncPromise<T = never>(reason: any): SyncPromise<T>;Usage Examples:
import { SyncPromise, resolvedSyncPromise, rejectedSyncPromise } from "@sentry/core";
// Basic SyncPromise usage - executes immediately
const syncPromise = new SyncPromise<number>((resolve, reject) => {
console.log('Executing immediately!'); // Logs immediately, not on next tick
resolve(42);
});
syncPromise.then(value => {
console.log('Value:', value); // Logs immediately after resolve
});
// Factory function usage
const resolved = resolvedSyncPromise('success');
const rejected = rejectedSyncPromise(new Error('failed'));
// Chaining works like regular promises
const result = resolvedSyncPromise(10)
.then(x => x * 2)
.then(x => x + 5)
.catch(err => console.error(err));
console.log(result); // SyncPromise, but value is computed immediately
// Converting async operations to sync for testing
function mockAsyncOperation(shouldFail: boolean): SyncPromise<string> {
if (shouldFail) {
return rejectedSyncPromise(new Error('Operation failed'));
}
return resolvedSyncPromise('Operation succeeded');
}Managed buffer for controlling concurrent promise execution with limits.
/**
* Buffer that manages concurrent promise execution
* @template T - Type of promise results
*/
interface PromiseBuffer<T> {
/** Array of currently running promises */
readonly $: Array<PromiseLike<T>>;
/**
* Adds a task to the buffer, respecting concurrency limits
* @param taskProducer - Function that creates the promise when ready to execute
* @returns Promise that resolves when the task completes
*/
add(taskProducer: () => PromiseLike<T>): PromiseLike<T>;
/**
* Waits for all currently running promises to complete
* @param timeout - Optional timeout in milliseconds
* @returns Promise that resolves to true if all tasks completed, false if timeout
*/
drain(timeout?: number): PromiseLike<boolean>;
}
/**
* Creates a new promise buffer with optional concurrency limit
* @param limit - Maximum number of concurrent promises (default: 30)
* @returns New promise buffer instance
*/
function makePromiseBuffer<T>(limit?: number): PromiseBuffer<T>;Usage Examples:
import { makePromiseBuffer } from "@sentry/core";
// Create buffer with concurrency limit
const buffer = makePromiseBuffer<string>(3); // Max 3 concurrent operations
// Add tasks to buffer - they'll execute when slots are available
const tasks = Array.from({ length: 10 }, (_, i) =>
buffer.add(() => fetchData(`item-${i}`))
);
// Wait for all tasks to complete
Promise.all(tasks).then(results => {
console.log('All tasks completed:', results);
});
// Example: Processing file uploads with concurrency control
class FileUploader {
private uploadBuffer = makePromiseBuffer<UploadResult>(5);
async uploadFiles(files: File[]): Promise<UploadResult[]> {
const uploadTasks = files.map(file =>
this.uploadBuffer.add(() => this.uploadSingleFile(file))
);
return Promise.all(uploadTasks);
}
private async uploadSingleFile(file: File): Promise<UploadResult> {
// Simulate file upload
const formData = new FormData();
formData.append('file', file);
const response = await fetch('/upload', {
method: 'POST',
body: formData
});
return response.json();
}
async waitForAllUploads(timeout = 30000): Promise<boolean> {
return this.uploadBuffer.drain(timeout);
}
}Utility for creating timeout mechanisms and operation monitoring.
/**
* Creates a watchdog timer that can be used to timeout operations
* @param callback - Function to call when timer expires
* @param delay - Delay in milliseconds before calling callback
* @returns Function to cancel the timer
*/
function watchdogTimer(callback: () => void, delay: number): () => void;Usage Examples:
import { watchdogTimer } from "@sentry/core";
// Basic timeout functionality
function withTimeout<T>(promise: Promise<T>, timeoutMs: number): Promise<T> {
return new Promise((resolve, reject) => {
const cancel = watchdogTimer(() => {
reject(new Error(`Operation timed out after ${timeoutMs}ms`));
}, timeoutMs);
promise
.then(resolve)
.catch(reject)
.finally(cancel); // Cancel timer when promise settles
});
}
// Usage with async operations
async function fetchWithTimeout(url: string): Promise<Response> {
const fetchPromise = fetch(url);
return withTimeout(fetchPromise, 5000); // 5 second timeout
}
// Periodic health checks
class HealthMonitor {
private cancelWatchdog?: () => void;
startMonitoring(intervalMs: number) {
const scheduleNext = () => {
this.cancelWatchdog = watchdogTimer(() => {
this.performHealthCheck()
.then(() => scheduleNext()) // Schedule next check
.catch(err => console.error('Health check failed:', err));
}, intervalMs);
};
scheduleNext();
}
stopMonitoring() {
this.cancelWatchdog?.();
}
private async performHealthCheck(): Promise<void> {
// Health check logic
const response = await fetch('/health');
if (!response.ok) {
throw new Error(`Health check failed: ${response.status}`);
}
}
}Pattern for testing and synchronous execution:
import { SyncPromise, resolvedSyncPromise, rejectedSyncPromise } from "@sentry/core";
function toSyncPromise<T>(promise: Promise<T>): SyncPromise<T> {
// Note: This breaks the asynchronous nature - use carefully
let result: T;
let error: any;
let isResolved = false;
let isRejected = false;
promise
.then(value => {
result = value;
isResolved = true;
})
.catch(err => {
error = err;
isRejected = true;
});
// Warning: This is a synchronous check and won't work for truly async operations
if (isResolved) {
return resolvedSyncPromise(result);
} else if (isRejected) {
return rejectedSyncPromise(error);
} else {
// For truly async operations, this approach won't work
throw new Error('Cannot convert async promise to sync');
}
}Using promise buffers for controlled batch processing:
import { makePromiseBuffer } from "@sentry/core";
class BatchProcessor<T, R> {
private buffer = makePromiseBuffer<R>(10);
async processBatch(items: T[], processor: (item: T) => Promise<R>): Promise<R[]> {
const tasks = items.map(item =>
this.buffer.add(() => processor(item))
);
return Promise.all(tasks);
}
async processWithRetry<T, R>(
items: T[],
processor: (item: T) => Promise<R>,
maxRetries = 3
): Promise<Array<R | Error>> {
const results: Array<R | Error> = [];
for (const item of items) {
const processWithRetry = async (): Promise<R> => {
let lastError: Error;
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
return await processor(item);
} catch (error) {
lastError = error as Error;
if (attempt < maxRetries) {
await new Promise(resolve => setTimeout(resolve, 1000 * Math.pow(2, attempt)));
}
}
}
throw lastError!;
};
try {
const result = await this.buffer.add(processWithRetry);
results.push(result);
} catch (error) {
results.push(error as Error);
}
}
return results;
}
}Using watchdog timers for circuit breaker implementation:
import { watchdogTimer } from "@sentry/core";
class CircuitBreaker {
private failureCount = 0;
private lastFailureTime = 0;
private state: 'CLOSED' | 'OPEN' | 'HALF_OPEN' = 'CLOSED';
constructor(
private failureThreshold = 5,
private timeout = 60000, // 1 minute
private retryTimeout = 10000 // 10 seconds
) {}
async execute<T>(operation: () => Promise<T>): Promise<T> {
if (this.state === 'OPEN') {
if (Date.now() - this.lastFailureTime < this.retryTimeout) {
throw new Error('Circuit breaker is OPEN');
} else {
this.state = 'HALF_OPEN';
}
}
try {
const result = await this.executeWithTimeout(operation);
this.onSuccess();
return result;
} catch (error) {
this.onFailure();
throw error;
}
}
private executeWithTimeout<T>(operation: () => Promise<T>): Promise<T> {
return new Promise((resolve, reject) => {
const cancel = watchdogTimer(() => {
reject(new Error('Operation timed out'));
}, this.timeout);
operation()
.then(resolve)
.catch(reject)
.finally(cancel);
});
}
private onSuccess(): void {
this.failureCount = 0;
this.state = 'CLOSED';
}
private onFailure(): void {
this.failureCount++;
this.lastFailureTime = Date.now();
if (this.failureCount >= this.failureThreshold) {
this.state = 'OPEN';
}
}
}interface UploadResult {
filename: string;
size: number;
url: string;
success: boolean;
}Migration Note: All promise utilities have been moved from @sentry/utils to @sentry/core. Update your imports accordingly.
Install with Tessl CLI
npx tessl i tessl/npm-sentry--utils