polling.ts 1.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667
  1. import {
  2. MAX_POLL_TIME_MS,
  3. POLL_INTERVAL_MS,
  4. STABLE_POLLS_THRESHOLD,
  5. } from '../config';
  6. export interface PollOptions {
  7. pollInterval?: number;
  8. maxPollTime?: number;
  9. stableThreshold?: number;
  10. signal?: AbortSignal;
  11. }
  12. export interface PollResult<T> {
  13. success: boolean;
  14. data?: T;
  15. timedOut?: boolean;
  16. aborted?: boolean;
  17. }
  18. /**
  19. * Generic polling utility that waits for a condition to be met.
  20. * Returns when the condition is satisfied or timeout/abort occurs.
  21. */
  22. export async function pollUntilStable<T>(
  23. fetchFn: () => Promise<T>,
  24. isStable: (current: T, previous: T | null, stableCount: number) => boolean,
  25. opts: PollOptions = {},
  26. ): Promise<PollResult<T>> {
  27. const pollInterval = opts.pollInterval ?? POLL_INTERVAL_MS;
  28. const maxPollTime = opts.maxPollTime ?? MAX_POLL_TIME_MS;
  29. const stableThreshold = opts.stableThreshold ?? STABLE_POLLS_THRESHOLD;
  30. const pollStart = Date.now();
  31. let previousData: T | null = null;
  32. let stablePolls = 0;
  33. while (Date.now() - pollStart < maxPollTime) {
  34. if (opts.signal?.aborted) {
  35. return { success: false, aborted: true };
  36. }
  37. await new Promise((r) => setTimeout(r, pollInterval));
  38. const currentData = await fetchFn();
  39. if (isStable(currentData, previousData, stablePolls)) {
  40. stablePolls++;
  41. if (stablePolls >= stableThreshold) {
  42. return { success: true, data: currentData };
  43. }
  44. } else {
  45. stablePolls = 0;
  46. }
  47. previousData = currentData;
  48. }
  49. return { success: false, timedOut: true, data: previousData ?? undefined };
  50. }
  51. /**
  52. * Simple delay utility
  53. */
  54. export function delay(ms: number): Promise<void> {
  55. return new Promise((r) => setTimeout(r, ms));
  56. }