mirror of
https://github.com/PR0M3TH3AN/bitvid.git
synced 2026-10-27 16:02:26 +00:00
Extracted `fetchListIncrementally` from `js/nostr/client.js` into a new `RelayBatchFetcher` class in `js/nostr/relayBatchFetcher.js` to reduce complexity in `NostrClient`. Moved `withRequestTimeout` to `js/utils/asyncUtils.js` to make it reusable and remove duplication. Updated `js/nostr/client.js` to use these new components while preserving existing behavior and specific logging for COUNT diagnostics. - Created `js/nostr/relayBatchFetcher.js` - Updated `js/utils/asyncUtils.js` - Refactored `js/nostr/client.js` - Verified with existing tests Co-authored-by: PR0M3TH3AN <[email protected]>
106 lines
2.6 KiB
JavaScript
106 lines
2.6 KiB
JavaScript
import { devLogger } from "./logger.js";
|
|
|
|
/**
|
|
* Maps over an iterable with a concurrency limit.
|
|
*
|
|
* @param {Iterable} iterable - The items to map over.
|
|
* @param {Function} mapper - The async mapping function.
|
|
* @param {Object} options - Options object.
|
|
* @param {number} options.concurrency - The concurrency limit (default: Infinity).
|
|
* @returns {Promise<Array>} - The results of the mapping function in original order.
|
|
*/
|
|
export async function pMap(iterable, mapper, { concurrency = Infinity } = {}) {
|
|
const results = [];
|
|
const iterator = iterable[Symbol.iterator]();
|
|
let index = 0;
|
|
let active = 0;
|
|
let done = false;
|
|
let rejected = false;
|
|
|
|
return new Promise((resolve, reject) => {
|
|
function next() {
|
|
if (rejected) return;
|
|
if (done && active === 0) {
|
|
resolve(results);
|
|
return;
|
|
}
|
|
|
|
while (active < concurrency && !done) {
|
|
const nextItem = iterator.next();
|
|
if (nextItem.done) {
|
|
done = true;
|
|
if (active === 0) resolve(results);
|
|
return;
|
|
}
|
|
|
|
const currentIndex = index++;
|
|
active++;
|
|
|
|
Promise.resolve(nextItem.value)
|
|
.then((value) => mapper(value, currentIndex))
|
|
.then((result) => {
|
|
results[currentIndex] = result;
|
|
active--;
|
|
next();
|
|
})
|
|
.catch((err) => {
|
|
rejected = true;
|
|
reject(err);
|
|
});
|
|
}
|
|
}
|
|
|
|
next();
|
|
});
|
|
}
|
|
|
|
export function withRequestTimeout(promise, timeoutMs, onTimeout, message = "Request timed out") {
|
|
const resolvedTimeout = Number(timeoutMs);
|
|
const effectiveTimeout =
|
|
Number.isFinite(resolvedTimeout) && resolvedTimeout > 0
|
|
? Math.floor(resolvedTimeout)
|
|
: 4000;
|
|
|
|
let timeoutId = null;
|
|
let settled = false;
|
|
|
|
return new Promise((resolve, reject) => {
|
|
timeoutId = setTimeout(() => {
|
|
if (settled) {
|
|
return;
|
|
}
|
|
settled = true;
|
|
if (typeof onTimeout === "function") {
|
|
try {
|
|
onTimeout();
|
|
} catch (cleanupError) {
|
|
devLogger.warn("[asyncUtils] Timeout cleanup failed:", cleanupError);
|
|
}
|
|
}
|
|
reject(new Error(message));
|
|
}, effectiveTimeout);
|
|
|
|
Promise.resolve(promise)
|
|
.then((value) => {
|
|
if (settled) {
|
|
return;
|
|
}
|
|
settled = true;
|
|
if (timeoutId) {
|
|
clearTimeout(timeoutId);
|
|
}
|
|
resolve(value);
|
|
})
|
|
.catch((error) => {
|
|
if (settled) {
|
|
return;
|
|
}
|
|
settled = true;
|
|
if (timeoutId) {
|
|
clearTimeout(timeoutId);
|
|
}
|
|
reject(error);
|
|
});
|
|
});
|
|
}
|