From 89095fcb7ceb96bb142ae43860e0626d3aea1f8b Mon Sep 17 00:00:00 2001 From: manish-rorotechnology Date: Mon, 9 Nov 2026 15:31:40 +0530 Subject: [PATCH] Stop a hung market request from leaving the table on its skeleton. Abort the fetch after 15 seconds, and still cancel it when the query is dropped. Co-authored-by: Cursor --- apps/web/components/markets/api.ts | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) diff --git a/apps/web/components/markets/api.ts b/apps/web/components/markets/api.ts index 24a4819b..4e092581 100644 --- a/apps/web/components/markets/api.ts +++ b/apps/web/components/markets/api.ts @@ -46,11 +46,23 @@ interface MarketsPage { coins: Coin[]; } +const REQUEST_TIMEOUT_MS = 15_000; + async function get(path: string, signal?: AbortSignal): Promise { - const response = await fetch(`${MARKET_API_URL}${path}`, { signal }); - if (response.status === 404) throw new NotFoundError(path); - if (!response.ok) throw new Error(`Market API ${path} responded ${response.status}`); - return response.json() as Promise; + const controller = new AbortController(); + const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS); + const onAbort = () => controller.abort(); + signal?.addEventListener("abort", onAbort, { once: true }); + if (signal?.aborted) controller.abort(); + try { + const response = await fetch(`${MARKET_API_URL}${path}`, { signal: controller.signal }); + if (response.status === 404) throw new NotFoundError(path); + if (!response.ok) throw new Error(`Market API ${path} responded ${response.status}`); + return response.json() as Promise; + } finally { + clearTimeout(timeout); + signal?.removeEventListener("abort", onAbort); + } } export class NotFoundError extends Error {}