import type { Fn } from '../../@aileron/declare/index.js'; /** * Schedules a callback function to execute in the next macrotask cycle with environment-optimized timing. * * Uses `setImmediate` (Node.js) or `setTimeout` (browsers) to ensure safe rendering in browsers. * This scheduling strategy guarantees that the browser has an opportunity to update the display * and process user interactions between task executions, preventing UI freezing and ensuring * smooth rendering performance. * * @param callback - Function to execute in the next macrotask cycle * @returns Numeric ID that can be used with `cancelMacrotask` to cancel execution * * @example * Basic macrotask scheduling: * ```typescript * import { scheduleMacrotask } from '@winglet/common-utils'; * * console.log('1: Synchronous code'); * * // Schedule macrotask * const taskId = scheduleMacrotask(() => { * console.log('4: Macrotask executed'); * }); * * // Schedule microtask for comparison * Promise.resolve().then(() => { * console.log('3: Microtask executed'); * }); * * console.log('2: More synchronous code'); * * // Output order: * // 1: Synchronous code * // 2: More synchronous code * // 3: Microtask executed * // 4: Macrotask executed * ``` * * @example * Event loop execution order demonstration: * ```typescript * const executionOrder: string[] = []; * * // Synchronous execution * executionOrder.push('sync-1'); * * // Macrotask (runs after microtasks) * scheduleMacrotask(() => { * executionOrder.push('macrotask-1'); * * // Nested microtask (runs before next macrotask) * queueMicrotask(() => { * executionOrder.push('nested-microtask'); * }); * * // Nested macrotask (runs in subsequent cycle) * scheduleMacrotask(() => { * executionOrder.push('nested-macrotask'); * }); * }); * * // Microtask (runs before macro tasks) * queueMicrotask(() => { * executionOrder.push('microtask-1'); * }); * * // Another macrotask * scheduleMacrotask(() => { * executionOrder.push('macrotask-2'); * }); * * executionOrder.push('sync-2'); * * // Final order: ['sync-1', 'sync-2', 'microtask-1', 'macrotask-1', 'macrotask-2', 'nested-microtask', 'nested-macrotask'] * ``` * * @example * UI updates and rendering coordination: * ```typescript * // Defer heavy computation to avoid blocking UI * function processLargeDataset(data: any[], callback: (result: any) => void) { * const batchSize = 1000; * let index = 0; * const results: any[] = []; * * function processBatch() { * const end = Math.min(index + batchSize, data.length); * * // Process batch synchronously * for (let i = index; i < end; i++) { * results.push(processItem(data[i])); * } * * index = end; * * if (index < data.length) { * // Schedule next batch to allow UI updates * scheduleMacrotask(processBatch); * } else { * callback(results); * } * } * * scheduleMacrotask(processBatch); * } * * // DOM manipulation coordination * function updateUIAfterDataChange(newData: any) { * // Update data model (synchronous) * updateModel(newData); * * // Schedule DOM updates for next macrotask * scheduleMacrotask(() => { * updateDOM(); * * // Schedule analytics after DOM is updated * scheduleMacrotask(() => { * trackUserInteraction('data-updated'); * }); * }); * } * ``` * * @example * Async task coordination: * ```typescript * // Coordinate multiple async operations * async function coordinatedExecution() { * console.log('Starting coordination'); * * // Immediate microtask * await Promise.resolve(); * console.log('After microtask'); * * // Schedule work for next macrotask * await new Promise(resolve => { * scheduleMacrotask(() => { * console.log('In macrotask'); * resolve(undefined); * }); * }); * * console.log('After macrotask'); * } * * // Testing and timing control * function createTestSequence() { * const events: string[] = []; * * return { * addSyncEvent: (name: string) => events.push(`sync:${name}`), * addMicrotaskEvent: (name: string) => { * queueMicrotask(() => events.push(`micro:${name}`)); * }, * addMacrotaskEvent: (name: string) => { * scheduleMacrotask(() => events.push(`macro:${name}`)); * }, * getEvents: () => [...events] * }; * } * ``` * * @example * Error handling and cleanup: * ```typescript * // Safe macrotask scheduling with error handling * function safeScheduleMacrotask(callback: () => void, errorHandler?: (error: Error) => void) { * return scheduleMacrotask(() => { * try { * callback(); * } catch (error) { * if (errorHandler) { * errorHandler(error as Error); * } else { * console.error('Macrotask error:', error); * } * } * }); * } * * // Resource cleanup coordination * function scheduleCleanup(resources: Resource[]) { * scheduleMacrotask(() => { * resources.forEach(resource => { * try { * resource.cleanup(); * } catch (error) { * console.warn('Cleanup error:', error); * } * }); * }); * } * ``` * * @remarks * **Why This Approach Ensures Safe Rendering:** * - **Yielding Control**: Both APIs yield control back to the browser's event loop * - **Rendering Opportunity**: Browser can perform layout, paint, and composite between tasks * - **Responsive UI**: User interactions are processed between scheduled callbacks * - **Frame Budget**: Respects the browser's 16ms frame budget for smooth 60fps rendering * * **Platform Behavior for Safe Rendering:** * - **Node.js (setImmediate)**: Executes in next event loop iteration, allowing I/O and rendering * - **Browsers (setTimeout)**: Minimum 4ms delay provides guaranteed rendering window * - **Background Tabs**: Automatic throttling prevents resource waste while maintaining safety * - **Mobile Browsers**: Optimized scheduling respects battery and performance constraints * * **Performance Characteristics:** * - **Time Complexity**: O(1) for scheduling operation * - **Space Complexity**: O(1) per scheduled task * - **Scheduling Overhead**: ~0.001ms in Node.js, ~0.01ms in browsers * - **Memory Usage**: Minimal, automatic cleanup after execution * * **Comparison with Alternatives:** * - **setTimeout(0)**: Similar but may have longer delays in browsers * - **Promise.resolve().then()**: Executes earlier (microtask queue) * - **requestAnimationFrame**: Tied to browser rendering, different timing * - **MessageChannel**: More complex setup, similar timing characteristics * * **Use Cases:** * - Breaking up long-running computations to avoid blocking * - Coordinating DOM updates with data changes * - Implementing custom scheduling systems * - Deferring non-critical operations * - Testing event loop behavior * - Coordinating with external async operations */ export declare const scheduleMacrotaskSafe: Fn<[callback: Fn<[], void>], number>; /** * Cancels a previously scheduled macrotask using its numeric identifier. * * Uses `clearImmediate` (Node.js) or `clearTimeout` (browsers) to cancel scheduled tasks. * This ensures that cancelled tasks don't interfere with browser rendering cycles and helps * maintain optimal rendering performance by preventing unnecessary task execution. * * @param id - Numeric identifier returned by `scheduleMacrotask` * * @example * Basic task cancellation: * ```typescript * import { scheduleMacrotask, cancelMacrotask } from '@winglet/common-utils'; * * // Schedule a task * const taskId = scheduleMacrotask(() => { * console.log('This will not execute'); * }); * * // Cancel before execution * cancelMacrotask(taskId); * * // Task is safely cancelled, no output * ``` * * @example * Conditional cancellation patterns: * ```typescript * // Timeout-based cancellation * function scheduleWithTimeout( * task: () => T, * timeoutMs: number * ): Promise { * return new Promise((resolve, reject) => { * let completed = false; * * // Schedule main task * const taskId = scheduleMacrotask(() => { * if (!completed) { * completed = true; * try { * resolve(task()); * } catch (error) { * reject(error); * } * } * }); * * // Schedule timeout cancellation * setTimeout(() => { * if (!completed) { * completed = true; * cancelMacrotask(taskId); * reject(new Error('Task timeout')); * } * }, timeoutMs); * }); * } * * // Condition-based cancellation * function scheduleConditional( * task: () => void, * condition: () => boolean * ) { * const taskId = scheduleMacrotask(() => { * if (condition()) { * task(); * } * }); * * return () => cancelMacrotask(taskId); * } * ``` * * @example * Resource management and cleanup: * ```typescript * // Cancellable batch processor * class BatchProcessor { * private taskIds: Set = new Set(); * private cancelled = false; * * processBatches(data: any[], batchSize: number) { * for (let i = 0; i < data.length; i += batchSize) { * const batch = data.slice(i, i + batchSize); * * const taskId = scheduleMacrotask(() => { * if (!this.cancelled) { * this.processBatch(batch); * this.taskIds.delete(taskId); * } * }); * * this.taskIds.add(taskId); * } * } * * cancel() { * this.cancelled = true; * this.taskIds.forEach(id => cancelMacrotask(id)); * this.taskIds.clear(); * } * * private processBatch(batch: any[]) { * // Process batch logic * } * } * * // Component lifecycle integration * class Component { * private scheduledTasks: number[] = []; * * scheduleUpdate(updateFn: () => void) { * const taskId = scheduleMacrotask(updateFn); * this.scheduledTasks.push(taskId); * return taskId; * } * * destroy() { * // Cancel all pending updates * this.scheduledTasks.forEach(cancelMacrotask); * this.scheduledTasks.length = 0; * } * } * ``` * * @example * Race condition prevention: * ```typescript * // Prevent multiple executions * class SingleExecutor { * private currentTaskId: number | null = null; * * schedule(task: () => void) { * // Cancel previous task if pending * if (this.currentTaskId !== null) { * cancelMacrotask(this.currentTaskId); * } * * // Schedule new task * this.currentTaskId = scheduleMacrotask(() => { * this.currentTaskId = null; * task(); * }); * * return this.currentTaskId; * } * * cancel() { * if (this.currentTaskId !== null) { * cancelMacrotask(this.currentTaskId); * this.currentTaskId = null; * return true; * } * return false; * } * } * * // Debounced scheduling * function createDebouncedScheduler(delayMs: number = 0) { * let taskId: number | null = null; * * return function schedule(task: () => void) { * if (taskId !== null) { * cancelMacrotask(taskId); * } * * taskId = scheduleMacrotask(() => { * taskId = null; * task(); * }); * * return taskId; * }; * } * ``` * * @remarks * **Cancellation Guarantees:** * - **Timing**: Safe to cancel at any point before task execution * - **Memory**: Automatic cleanup of cancelled tasks * - **Safety**: No errors thrown for invalid or already-executed IDs * - **Atomicity**: Cancellation is immediate and irreversible * * **Platform Behavior:** * - **Node.js**: Uses `clearImmediate` for tasks scheduled with `setImmediate` * - **Browsers**: Uses `clearTimeout` for tasks scheduled with `setTimeout` * - **Cross-platform**: Consistent API regardless of underlying implementation * - **Error Handling**: Silent failure for invalid IDs (matches native behavior) * * **Performance Characteristics:** * - **Time Complexity**: O(1) for cancellation operation * - **Space Complexity**: O(1) per cancelled task * - **Overhead**: Negligible cancellation cost * - **Memory**: Immediate cleanup of cancelled task references * * **Best Practices:** * - Store task IDs when cancellation might be needed * - Cancel tasks during component/object cleanup * - Use cancellation for timeout implementations * - Consider cancellation in error handling paths * - Don't rely on cancellation for security-critical operations */ export declare const cancelMacrotaskSafe: Fn<[id: number], void>; /** * Creates a cancellable macrotask with a fluent API that returns a cancellation function. * * Combines `setImmediate`/`setTimeout` scheduling with cancellation support to ensure safe * browser rendering. The returned cancellation function allows tasks to be cancelled before * execution, preventing unnecessary work and maintaining smooth UI performance by not * blocking rendering cycles with cancelled tasks. * * @param callback - Function to execute in the next macrotask cycle * @returns Cancellation function that prevents execution when called * * @example * Basic cancellable task: * ```typescript * import { scheduleCancelableMacrotask } from '@winglet/common-utils'; * * // Schedule a task and get cancellation function * const cancelTask = scheduleCancelableMacrotask(() => { * console.log('This may or may not execute'); * }); * * // Cancel the task before execution * cancelTask(); * // No output - task was cancelled * * // Alternative: let it execute naturally * const cancelTask2 = scheduleCancelableMacrotask(() => { * console.log('This will execute'); * }); * // Don't call cancelTask2() - output: "This will execute" * ``` * * @example * Conditional cancellation patterns: * ```typescript * // Auto-cancelling timeout pattern * function createAutoTimeout(callback: () => void, timeoutMs: number) { * const cancelTask = scheduleCancelableMacrotask(callback); * * setTimeout(() => { * cancelTask(); // Auto-cancel after timeout * }, timeoutMs); * * return cancelTask; // Return manual cancellation option * } * * // Conditional execution with early cancellation * function scheduleConditionalWork( * work: () => void, * condition: () => boolean * ) { * const cancelTask = scheduleCancelableMacrotask(() => { * if (condition()) { * work(); * } * }); * * // Cancel immediately if condition is already false * if (!condition()) { * cancelTask(); * } * * return cancelTask; * } * * // User interaction cancellation * function scheduleWithUserCancellation(task: () => void) { * const cancelTask = scheduleCancelableMacrotask(task); * * // Allow user to cancel via UI * const button = document.createElement('button'); * button.textContent = 'Cancel Task'; * button.onclick = () => { * cancelTask(); * button.disabled = true; * button.textContent = 'Task Cancelled'; * }; * * document.body.appendChild(button); * return cancelTask; * } * ``` * * @example * Resource management and cleanup: * ```typescript * // Component lifecycle integration * class AsyncComponent { * private pendingTasks: (() => void)[] = []; * * scheduleWork(work: () => void) { * const cancelTask = scheduleCancelableMacrotask(work); * this.pendingTasks.push(cancelTask); * return cancelTask; * } * * destroy() { * // Cancel all pending tasks * this.pendingTasks.forEach(cancel => cancel()); * this.pendingTasks.length = 0; * } * } * * // Batch processor with individual task cancellation * class CancellableBatchProcessor { * private activeTasks = new Map void>(); * * processItem(id: string, processor: () => void) { * // Cancel existing task for this ID if any * const existingCancel = this.activeTasks.get(id); * if (existingCancel) { * existingCancel(); * } * * // Schedule new task * const cancelTask = scheduleCancelableMacrotask(() => { * processor(); * this.activeTasks.delete(id); * }); * * this.activeTasks.set(id, cancelTask); * return cancelTask; * } * * cancelItem(id: string): boolean { * const cancelTask = this.activeTasks.get(id); * if (cancelTask) { * cancelTask(); * this.activeTasks.delete(id); * return true; * } * return false; * } * * cancelAll() { * this.activeTasks.forEach(cancel => cancel()); * this.activeTasks.clear(); * } * } * ``` * * @example * Async coordination and chaining: * ```typescript * // Chainable cancellable tasks * function createTaskChain(...tasks: (() => void)[]) { * const cancellations: (() => void)[] = []; * let chainCancelled = false; * * function scheduleNext(index: number) { * if (chainCancelled || index >= tasks.length) return; * * const cancelTask = scheduleCancelableMacrotask(() => { * if (!chainCancelled) { * tasks[index](); * scheduleNext(index + 1); * } * }); * * cancellations.push(cancelTask); * } * * scheduleNext(0); * * return () => { * chainCancelled = true; * cancellations.forEach(cancel => cancel()); * }; * } * * // Promise-based cancellable scheduling * function createCancellablePromise( * executor: () => T * ): { promise: Promise; cancel: () => void } { * let cancelled = false; * * const promise = new Promise((resolve, reject) => { * const cancelTask = scheduleCancelableMacrotask(() => { * if (!cancelled) { * try { * resolve(executor()); * } catch (error) { * reject(error); * } * } * }); * * // Store cancellation for external access * promise.cancel = () => { * cancelled = true; * cancelTask(); * reject(new Error('Task was cancelled')); * }; * }); * * return { * promise, * cancel: () => (promise as any).cancel() * }; * } * ``` * * @example * Error handling and safety: * ```typescript * // Safe execution with error handling * function createSafeCancellableTask( * task: () => void, * errorHandler: (error: Error) => void = console.error * ) { * return scheduleCancelableMacrotask(() => { * try { * task(); * } catch (error) { * errorHandler(error as Error); * } * }); * } * * // Multiple cancellation safety * function createIdempotentCancellation(task: () => void) { * let cancelled = false; * let originalCancel: (() => void) | null = null; * * originalCancel = scheduleCancelableMacrotask(() => { * if (!cancelled) { * task(); * } * }); * * return () => { * if (!cancelled && originalCancel) { * cancelled = true; * originalCancel(); * originalCancel = null; * } * }; * } * * // Timeout with automatic cancellation * function scheduleWithDeadline( * task: () => void, * deadlineMs: number * ): { cancel: () => void; expired: () => boolean } { * let expired = false; * let executed = false; * * const cancelTask = scheduleCancelableMacrotask(() => { * if (!expired) { * executed = true; * task(); * } * }); * * const timeoutId = setTimeout(() => { * if (!executed) { * expired = true; * cancelTask(); * } * }, deadlineMs); * * return { * cancel: () => { * clearTimeout(timeoutId); * cancelTask(); * }, * expired: () => expired * }; * } * ``` * * @remarks * **Cancellation Strategy:** * - **Two-Layer Protection**: Platform cancellation + execution guard boolean * - **Race Condition Safe**: Handles cancellation before and during execution * - **Memory Efficient**: Automatic cleanup of cancelled tasks * - **Idempotent**: Safe to call cancellation function multiple times * * **Internal Implementation:** * - **Scheduling**: Uses `scheduleMacrotask` for platform-optimized timing * - **Cancellation**: Combines `cancelMacrotask` with boolean flag * - **State Management**: Minimal state tracking for optimal performance * - **Cleanup**: Automatic garbage collection of completed/cancelled tasks * * **Performance Characteristics:** * - **Time Complexity**: O(1) for scheduling and cancellation * - **Space Complexity**: O(1) per scheduled task * - **Memory Overhead**: ~40 bytes per task (closure + boolean flag) * - **Cancellation Speed**: Immediate (no async overhead) * * **Comparison with Alternatives:** * - **AbortController**: More complex API, larger memory footprint * - **Promise cancellation**: Requires additional promise infrastructure * - **Manual ID tracking**: More error-prone, requires external state management * - **setTimeout with clearTimeout**: Similar performance, less ergonomic API * * **Event Loop Integration:** * - **Execution Timing**: Same as `scheduleMacrotask` (after microtasks) * - **Cancellation Timing**: Immediate, no event loop involvement * - **Platform Behavior**: Inherits all platform-specific optimizations * - **Nested Scheduling**: Full support for task scheduling within tasks * * **Use Cases:** * - Component cleanup and lifecycle management * - User interaction cancellation (cancel buttons, navigation) * - Timeout and deadline management * - Batch processing with individual item cancellation * - Resource loading with cancellation support * - Animation and transition management * - Testing and development tools * * **Best Practices:** * - Store cancellation functions when cleanup is needed * - Call cancellation in component/object destroy methods * - Use with timeout patterns for deadline management * - Combine with error handling for robust task execution * - Consider memory implications with large numbers of pending tasks * - Test cancellation paths in your application logic * * **Thread Safety and Concurrency:** * - **Single-threaded**: Safe in JavaScript's single-threaded environment * - **Async Safety**: Safe to cancel from different async contexts * - **Re-entrance**: Safe to call cancellation from within task callbacks * - **Memory Model**: No memory ordering concerns in JavaScript */ export declare const scheduleCancelableMacrotaskSafe: (callback: Fn) => Fn;