@hoover-institution/hubspot-lib
Version:
A toolkit for deep integration with HubSpot's Marketing Events API with a plugin-based architecture.
98 lines (91 loc) • 2.95 kB
JavaScript
/**
* Polls any async function until a condition is met or retries run out.
* @param {Function} fn - The async function to call (should return a result).
* @param {Function} condition - Function(result): boolean, determines if polling should stop.
* @param {Array} args - Arguments to pass to the async function.
* @param {number} maxRetries - Maximum number of attempts.
* @param {number} delayMs - Delay between attempts (ms).
* @returns {Promise<any>} The result from fn if condition passes, otherwise throws.
*/
async function pollUntil(
fn,
condition,
args = [],
maxRetries = 6,
delayMs = 1000
) {
for (let attempt = 1; attempt <= maxRetries; attempt++) {
const result = await fn(...args);
if (condition(result)) return result;
if (attempt < maxRetries) {
console.log(
`⏳ Attempt ${attempt}/${maxRetries} did not meet condition, retrying in ${delayMs}ms`
);
await new Promise((res) => setTimeout(res, delayMs));
}
}
throw new Error("Condition not met after polling.");
}
export { pollUntil };
/**
* Displays a simple console spinner while an async operation runs.
* @param {Promise<any>} promise - The promise to wait for.
* @param {string} [message] - Optional message to display.
* @returns {Promise<any>} Resolves with the promise's result.
*/
async function withSpinner(promise, message = "Processing") {
const spinnerFrames = ["|", "/", "-", "\\"];
let i = 0;
let active = true;
const spinner = setInterval(() => {
process.stdout.write(
`\r${spinnerFrames[i++ % spinnerFrames.length]} ${message}`
);
}, 100);
try {
const result = await promise;
active = false;
clearInterval(spinner);
process.stdout.write(`\r✅${message}\n`);
return result;
} catch (err) {
active = false;
clearInterval(spinner);
process.stdout.write(`\r❌${message}\n`);
throw err;
}
}
export { withSpinner };
/**
* Waits for a specified amount of time, displaying a spinner.
* @param {number} ms - The number of milliseconds to wait.
* @returns {Promise<void>} Resolves after the specified delay.
*/
async function wait(ms) {
function msToReadable(ms) {
if (ms < 1000) return `${ms}ms`;
const sec = Math.floor(ms / 1000) % 60;
const min = Math.floor(ms / 60000);
let str = "";
if (min > 0) str += `${min}m `;
if (sec > 0) str += `${sec}s`;
return str.trim();
}
const readable = msToReadable(ms);
await withSpinner(
new Promise((resolve) => setTimeout(resolve, ms)),
`Processing the request for ${readable}`
);
}
export { wait };
/**
* Waits for a specified time, then runs the provided function.
* @param {Function} fn - The function to execute after waiting.
* @param {number} ms - The number of milliseconds to wait.
* @returns {Promise<any>} Resolves with the result of fn.
*/
async function execDelay(fn, ms) {
await wait(ms);
return fn();
}
export { execDelay };