Skip to content

Commit 0ea0c63

Browse files
authored
Update loadData function. (#1180)
Fix #1175. We add retries when we fetch data. We checks that fetched json is valid and it is not empty. Reduce calls for the same urls.
1 parent 3aaa004 commit 0ea0c63

File tree

1 file changed

+82
-14
lines changed

1 file changed

+82
-14
lines changed

src/utils/dataLoader.ts

+82-14
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,90 @@
1-
export async function loadData(apiUrl: any) {
1+
// Define a more specific response type or use generics
2+
type DataResponse = unknown;
3+
4+
const dataCache: Record<string, DataResponse> = {};
5+
const inFlightCache: Record<string, Promise<DataResponse>> = {};
6+
7+
export async function loadData(
8+
apiUrl: string,
9+
maxRetries = 3,
10+
retryDelay = 1000
11+
): Promise<DataResponse> {
212
if (!apiUrl) {
3-
console.warn(`No API URL provided`);
4-
return {};
13+
throw new Error(`No API URL provided`);
514
}
615

7-
try {
8-
console.log(`Fetching data from: ${apiUrl}`);
9-
const response = await fetch(apiUrl);
16+
if (dataCache[apiUrl]) {
17+
console.log(`✅ Cache hit for: ${apiUrl}`);
18+
return dataCache[apiUrl];
19+
}
20+
21+
if (inFlightCache[apiUrl]) {
22+
console.log(`⏳ Waiting for in-flight fetch of: ${apiUrl}`);
23+
return inFlightCache[apiUrl];
24+
}
25+
26+
const fetchPromise = (async () => {
27+
let lastError: Error | null = null;
28+
29+
for (let attempt = 1; attempt <= maxRetries; attempt++) {
30+
try {
31+
console.log(`📡 Fetching data from: ${apiUrl} (Attempt ${attempt})`);
32+
const response = await fetch(apiUrl);
1033

11-
if (!response.ok) {
12-
throw new Error(
13-
`Failed to fetch data: ${response.status} ${response.statusText}`
14-
);
34+
if (!response.ok) {
35+
throw new Error(
36+
`Fetch failed: ${response.status} ${response.statusText}`
37+
);
38+
}
39+
40+
const data = await response.json();
41+
42+
if (
43+
data == null ||
44+
(typeof data === "object" &&
45+
!Array.isArray(data) &&
46+
Object.keys(data).length === 0)
47+
) {
48+
throw new Error(`Received empty or invalid JSON`);
49+
}
50+
51+
if (Array.isArray(data)) {
52+
console.log(`📦 Received JSON array with ${data.length} items`);
53+
} else if (typeof data === "object") {
54+
console.log(
55+
`📦 Received JSON object with ${Object.keys(data).length} keys`
56+
);
57+
}
58+
59+
// Store in final cache
60+
dataCache[apiUrl] = data;
61+
return data;
62+
} catch (error) {
63+
lastError = error instanceof Error ? error : new Error(String(error));
64+
console.error(`❌ Attempt ${attempt} failed:`, error);
65+
66+
if (attempt < maxRetries) {
67+
await new Promise((res) => setTimeout(res, retryDelay));
68+
} else {
69+
throw new Error(
70+
`Failed to load data from ${apiUrl} after ${maxRetries} attempts: ${lastError.message}`,
71+
{ cause: lastError }
72+
);
73+
}
74+
}
1575
}
1676

17-
return await response.json();
18-
} catch (error) {
19-
console.error(`Error loading data:`, error);
20-
return {};
77+
// This ensures TypeScript knows we always return something or throw
78+
throw new Error(`Unexpected execution path in loadData for ${apiUrl}`);
79+
})();
80+
81+
// Store the in-flight promise
82+
inFlightCache[apiUrl] = fetchPromise;
83+
84+
try {
85+
return await fetchPromise;
86+
} finally {
87+
// Remove from in-flight cache once done
88+
delete inFlightCache[apiUrl];
2189
}
2290
}

0 commit comments

Comments
 (0)