@speckle/objectloader2
Version:
This is an updated objectloader for the Speckle viewer written in typescript
117 lines • 3.88 kB
JavaScript
import KeyedQueue from './keyedQueue.js';
/**
* Default wait time in milliseconds for processing ongoing tasks during disposal.
* This value was chosen to balance responsiveness and CPU usage in typical scenarios.
*/
const PROCESSING_WAIT_TIME_MS = 100;
export default class BatchingQueue {
#queue = new KeyedQueue();
#batchSize;
#processFunction;
#timeoutId = null;
#isProcessing = false;
#logger;
#disposed = false;
#batchTimeout;
// Helper methods for cross-environment timeout handling
#getSetTimeoutFn() {
// First check for window object (browser), then fallback to global (node), then just use setTimeout
return typeof window !== 'undefined'
? window.setTimeout.bind(window)
: typeof global !== 'undefined'
? global.setTimeout
: setTimeout;
}
#getClearTimeoutFn() {
// First check for window object (browser), then fallback to global (node), then just use clearTimeout
return typeof window !== 'undefined'
? window.clearTimeout.bind(window)
: typeof global !== 'undefined'
? global.clearTimeout
: clearTimeout;
}
constructor(params) {
this.#batchSize = params.batchSize;
this.#processFunction = params.processFunction;
this.#batchTimeout = params.maxWaitTime;
this.#logger = params.logger || (() => { });
}
async disposeAsync() {
this.#disposed = true;
if (this.#timeoutId) {
this.#getClearTimeoutFn()(this.#timeoutId);
this.#timeoutId = null;
}
// Wait for any ongoing processing to finish
while (this.#isProcessing) {
await new Promise((resolve) => this.#getSetTimeoutFn()(resolve, PROCESSING_WAIT_TIME_MS));
}
// After any ongoing flush is completed, there might be items in the queue.
// We should flush them.
if (this.#queue.size > 0) {
await this.#flush();
}
}
add(key, item) {
if (this.#disposed)
return;
this.#queue.enqueue(key, item);
this.#addCheck();
}
addAll(keys, items) {
if (this.#disposed)
return;
this.#queue.enqueueAll(keys, items);
this.#addCheck();
}
#addCheck() {
if (this.#disposed)
return;
if (this.#queue.size >= this.#batchSize) {
// Fire and forget, no need to await
// eslint-disable-next-line @typescript-eslint/no-floating-promises
this.#flush();
}
else {
if (this.#timeoutId) {
this.#getClearTimeoutFn()(this.#timeoutId);
}
// eslint-disable-next-line @typescript-eslint/no-misused-promises
this.#timeoutId = this.#getSetTimeoutFn()(() => this.#flush(), this.#batchTimeout);
}
}
async #flush() {
if (this.#timeoutId) {
this.#getClearTimeoutFn()(this.#timeoutId);
this.#timeoutId = null;
}
if (this.#isProcessing || this.#queue.size === 0) {
return;
}
this.#isProcessing = true;
const batchToProcess = this.#getBatch(this.#batchSize);
try {
await this.#processFunction(batchToProcess);
}
catch (error) {
this.#logger('Batch processing failed:', error);
}
finally {
this.#isProcessing = false;
}
this.#addCheck();
}
get(id) {
return this.#queue.get(id);
}
count() {
return this.#queue.size;
}
isDisposed() {
return this.#disposed;
}
#getBatch(batchSize) {
return this.#queue.spliceValues(0, Math.min(batchSize, this.#queue.size));
}
}
//# sourceMappingURL=batchingQueue.js.map