All files / src/primitives lisAsync.ts

100% Statements 36/36
100% Branches 9/9
100% Functions 3/3
100% Lines 36/36

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 861x                                                                                 1x 6x 6x 6x 6x   1x 1x   5x     5x 1x 1x 5x   5x 2x 2x   2x 1x 1x 1x 1x 2x   5x 1x 1x 1x 1x   5x   5x 6x 1x 1x 1x 1x 6x 6x  
import LISWorker from './lis.worker.ts?worker';
 
/**
 * Response type from the LIS Web Worker.
 * Either returns the computed LIS indices or an error object.
 */
type WorkerResponse = number[] | { error: string };
 
/**
 * Computes the Longest Increasing Subsequence (LIS) asynchronously using a Web Worker.
 *
 * This function is recommended for large arrays (>1000 elements) to prevent blocking
 * the main UI thread during computationally intensive LIS calculations. It transfers
 * the TypedArray to a worker thread for processing and returns a Promise that resolves
 * with the LIS indices.
 *
 * The function uses zero-copy transfer of the TypedArray buffer to the worker for
 * optimal performance. A timeout prevents hanging computations.
 *
 * @param seq The sequence of numbers as a TypedArray for efficient transfer to worker
 * @returns Promise resolving to array of LIS indices, or rejecting on error/timeout
 * @throws Error if worker computation fails or times out after 30 seconds
 *
 * @example
 * ```typescript
 * // For large lists that might block the UI
 * const largeList = new Uint32Array(5000);
 * // ... populate array ...
 *
 * try {
 *   const lisIndices = await longestIncreasingSubsequenceAsync(largeList);
 *   console.log('LIS indices:', lisIndices);
 * } catch (error) {
 *   console.error('LIS computation failed:', error);
 * }
 *
 * // Compare with sync version for small arrays
 * const smallList = [3, 1, 4, 1, 5];
 * const syncResult = longestIncreasingSubsequence(smallList); // Faster for small arrays
 * ```
 */
export function longestIncreasingSubsequenceAsync(
  seq: Int32Array | Uint32Array | Float32Array | Float64Array
): Promise<number[]> {
  return new Promise((resolve, reject) => {
    if (!seq || seq.length === 0) {
      // Handle empty input gracefully without creating a worker.
      return resolve([]);
    }
 
    const worker = new LISWorker();
 
    // Set a timeout to prevent hanging workers.
    const timeout = setTimeout(() => {
      worker.terminate();
      reject(new Error('Worker timeout: LIS computation took too long.'));
    }, 30000); // 30-second timeout
 
    worker.onmessage = (event: MessageEvent<WorkerResponse>) => {
      clearTimeout(timeout);
      worker.terminate();
      
      if (event.data && typeof event.data === 'object' && 'error' in event.data) {
        reject(new Error(event.data.error));
      } else {
        resolve(event.data as number[]);
      }
    };
 
    worker.onerror = (error: ErrorEvent) => {
      clearTimeout(timeout);
      worker.terminate();
      reject(new Error(`Worker error: ${error.message}`));
    };
 
    try {
      // Post the sequence to the worker with a transferable object.
      worker.postMessage(seq, [seq.buffer]);
    } catch (error) {
      clearTimeout(timeout);
      worker.terminate();
      reject(error);
    }
  });
}