Non-blocking search operations using Web Workers for maintaining UI responsiveness during intensive search operations in browser environments. The Worker class extends Promise to provide seamless asynchronous search capabilities.
Web Worker implementation that creates search indexes in background threads to prevent UI blocking during intensive operations.
/**
* Web Worker implementation for non-blocking search operations
* @param options - Worker configuration options or preset
*/
class Worker extends Promise {
constructor(options?: Preset | WorkerIndexOptions);
}All search operations in worker threads return Promises, maintaining non-blocking execution.
/**
* Add document to worker-based index
* @param id - Document identifier
* @param content - Text content to index
* @returns Promise resolving when document is added
*/
add(id: Id, content: string): Promise<this>;
/**
* Search worker-based index
* @param query - Search query string
* @param options - Search configuration options
* @returns Promise resolving to search results
*/
search(query: string, options?: SearchOptions): Promise<Id[]>;
/**
* Remove document from worker-based index
* @param id - Document identifier to remove
* @returns Promise resolving when document is removed
*/
remove(id: Id): Promise<this>;
/**
* Update existing document in worker-based index
* @param id - Document identifier
* @param content - Updated text content
* @returns Promise resolving when document is updated
*/
update(id: Id, content: string): Promise<this>;
/**
* Clear all documents from worker-based index
* @returns Promise resolving when index is cleared
*/
clear(): Promise<void>;Usage Examples:
import { Worker } from "flexsearch";
// Create worker-based search index
const workerIndex = new Worker({
tokenize: "forward",
cache: true,
resolution: 9
});
// All operations are Promise-based
async function setupWorkerSearch() {
// Add documents without blocking UI
await workerIndex.add(1, "Large document with lots of content that might block UI rendering");
await workerIndex.add(2, "Another substantial document for background processing");
await workerIndex.add(3, "Complex text requiring intensive tokenization and processing");
console.log("All documents added in background");
// Search in background thread
const results = await workerIndex.search("complex");
console.log("Search results:", results); // [3]
}
// Non-blocking initialization
setupWorkerSearch().then(() => {
console.log("Worker search setup complete");
});
// UI remains responsive during search operationsConfigure worker-specific options and performance settings.
interface WorkerIndexOptions extends IndexOptions {
/** Worker script URL or path */
worker?: string | WorkerScript;
/** Worker pool size */
poolSize?: number;
/** Maximum worker idle time before termination */
idleTimeout?: number;
/** Worker communication timeout */
timeout?: number;
/** Enable worker debugging */
debug?: boolean;
}
interface WorkerScript {
/** Worker script URL */
url: string;
/** Worker script type */
type?: "classic" | "module";
/** Worker credentials */
credentials?: "omit" | "same-origin" | "include";
}Usage Examples:
// Worker with custom configuration
const customWorker = new Worker({
tokenize: "bidirectional",
encoder: "LatinAdvanced",
cache: 1000,
worker: {
url: "/js/search-worker.js",
type: "module",
credentials: "same-origin"
},
poolSize: 4,
idleTimeout: 30000,
timeout: 10000,
debug: process.env.NODE_ENV === "development"
});Worker class extends Promise, enabling advanced async patterns and integration with Promise-based workflows.
Usage Examples:
// Worker as Promise for initialization
const workerIndex = new Worker()
.then(worker => {
console.log("Worker initialized successfully");
return worker;
})
.catch(error => {
console.error("Worker initialization failed:", error);
});
// Use with async/await
async function performSearch(query) {
try {
const worker = await new Worker();
// Add documents
await Promise.all([
worker.add(1, "First document content"),
worker.add(2, "Second document content"),
worker.add(3, "Third document content")
]);
// Search
const results = await worker.search(query);
return results;
} catch (error) {
console.error("Search operation failed:", error);
return [];
}
}
// Chain operations
performSearch("document")
.then(results => {
console.log("Search completed:", results);
})
.catch(error => {
console.error("Search failed:", error);
});Perform multiple operations efficiently in worker threads.
Usage Examples:
// Batch document addition
async function addDocumentsBatch(documents) {
const worker = new Worker();
// Process documents in batches to avoid overwhelming the worker
const batchSize = 100;
for (let i = 0; i < documents.length; i += batchSize) {
const batch = documents.slice(i, i + batchSize);
// Add batch of documents concurrently
await Promise.all(
batch.map((doc, index) =>
worker.add(i + index, doc.content)
)
);
console.log(`Processed batch ${Math.floor(i / batchSize) + 1}`);
}
return worker;
}
// Usage
const documents = [
{ content: "Document 1 content..." },
{ content: "Document 2 content..." },
// ... many more documents
];
addDocumentsBatch(documents)
.then(worker => {
console.log("All documents added via worker");
return worker.search("content");
})
.then(results => {
console.log("Search results:", results);
});Serialize and restore worker-based indexes for data persistence.
/**
* Export worker index data
* @returns Promise resolving to exported data
*/
export(): Promise<void>;
/**
* Import data into worker index
* @returns Promise resolving when import is complete
*/
import(): Promise<void>;Usage Examples:
// Export worker index
async function exportWorkerIndex(worker) {
const exportData = {};
await worker.export((key, data) => {
exportData[key] = data;
});
// Save to localStorage or send to server
localStorage.setItem('workerIndex', JSON.stringify(exportData));
return exportData;
}
// Import worker index
async function importWorkerIndex() {
const worker = new Worker();
const exportData = JSON.parse(localStorage.getItem('workerIndex') || '{}');
// Import data into worker
for (const [key, data] of Object.entries(exportData)) {
await worker.import(key, data);
}
return worker;
}
// Usage
const worker = await new Worker();
// ... add documents and perform operations
// Export for later use
await exportWorkerIndex(worker);
// Later, restore the index
const restoredWorker = await importWorkerIndex();
const results = await restoredWorker.search("query");Handle worker-specific errors and communication failures gracefully.
Usage Examples:
// Robust worker error handling
async function createRobustWorker(options) {
let worker;
let retryCount = 0;
const maxRetries = 3;
while (retryCount < maxRetries) {
try {
worker = new Worker(options);
// Test worker communication
await worker.add(0, "test");
await worker.remove(0);
console.log("Worker created successfully");
break;
} catch (error) {
retryCount++;
console.warn(`Worker creation attempt ${retryCount} failed:`, error);
if (retryCount >= maxRetries) {
throw new Error(`Failed to create worker after ${maxRetries} attempts`);
}
// Wait before retry
await new Promise(resolve => setTimeout(resolve, 1000 * retryCount));
}
}
return worker;
}
// Use robust worker creation
createRobustWorker({
tokenize: "forward",
cache: true
})
.then(worker => {
console.log("Robust worker ready");
return worker.search("test");
})
.catch(error => {
console.error("Worker creation failed:", error);
// Fallback to non-worker search
const fallbackIndex = new Index();
return fallbackIndex;
});Manage multiple workers for high-throughput search applications.
Usage Examples:
// Simple worker pool implementation
class WorkerPool {
constructor(size = 4, options = {}) {
this.size = size;
this.options = options;
this.workers = [];
this.currentWorker = 0;
}
async initialize() {
for (let i = 0; i < this.size; i++) {
const worker = new Worker(this.options);
this.workers.push(worker);
}
// Wait for all workers to be ready
await Promise.all(this.workers);
console.log(`Worker pool initialized with ${this.size} workers`);
}
getNextWorker() {
const worker = this.workers[this.currentWorker];
this.currentWorker = (this.currentWorker + 1) % this.size;
return worker;
}
async addDocument(id, content) {
const worker = this.getNextWorker();
return worker.add(id, content);
}
async search(query, options) {
// Search all workers and combine results
const searchPromises = this.workers.map(worker =>
worker.search(query, options).catch(() => [])
);
const allResults = await Promise.all(searchPromises);
// Combine and deduplicate results
const combined = new Set();
allResults.forEach(results => {
results.forEach(id => combined.add(id));
});
return Array.from(combined);
}
async terminate() {
// Terminate all workers
this.workers.forEach(worker => {
if (worker.terminate) {
worker.terminate();
}
});
}
}
// Usage
const pool = new WorkerPool(4, {
tokenize: "forward",
cache: true
});
await pool.initialize();
// Add documents across worker pool
await Promise.all([
pool.addDocument(1, "Content for worker pool"),
pool.addDocument(2, "Another document"),
pool.addDocument(3, "Third document")
]);
// Search across all workers
const results = await pool.search("content");
console.log("Pool search results:", results);
// Clean up
await pool.terminate();interface WorkerIndexOptions extends IndexOptions {
/** Worker script configuration */
worker?: string | WorkerScript;
/** Number of workers in pool */
poolSize?: number;
/** Worker idle timeout in milliseconds */
idleTimeout?: number;
/** Worker operation timeout in milliseconds */
timeout?: number;
/** Enable worker debugging */
debug?: boolean;
/** Worker error handling strategy */
errorHandling?: "retry" | "fallback" | "throw";
/** Maximum retry attempts for failed operations */
maxRetries?: number;
/** Worker memory limit */
memoryLimit?: number;
}type WorkerType = boolean | Worker;
interface WorkerScript {
url: string;
type?: "classic" | "module";
credentials?: "omit" | "same-origin" | "include";
}
interface WorkerStatus {
id: string;
ready: boolean;
idle: boolean;
memoryUsage: number;
taskCount: number;
lastActivity: number;
}
interface WorkerError extends Error {
workerId: string;
operation: string;
retryable: boolean;
}